함수형 프로그래밍

메이비 모나드를 활용한 안전한 널 처리 (함수형 프로그래밍)

태인킴 2024. 3. 3. 12:29
반응형

코틀린은 널 가능성을 타입 시스템에 내장하여 널 참조 오류(NullPointerException)를 줄이는 데 도움을 줍니다. 그러나 복잡한 데이터 구조에서 널을 처리하는 것은 여전히 도전적일 수 있습니다. 이 글에서는 메이비 모나드(Maybe Monad)를 사용하여 이러한 문제를 어떻게 해결할 수 있는지 살펴봅니다.

기본 예제: 중첩된 널 체크

먼저, 중첩된 클래스 구조에서 널을 처리하는 기본적인 방법을 살펴보겠습니다.

class A1(val b: B1?)
class B1(val c: C1?)
class C1(val d: D1?)
class D1(val value: String?)

fun getValueOfD1(a: A1): String {
    val b = a.b
    if (b != null) {
        val c = b.c
        if (c != null) {
            val d = c.d
            if (d != null) {
                if (d.value != null) {
                    return d.value
                } else {
                    return ""
                }
            }
        }
    }
    return ""
}

메이비 모나드를 사용한 개선

메이비 모나드를 사용하면 이러한 중첩된 널 체크를 훨씬 깔끔하게 처리할 수 있습니다. 메이비(Maybe)는 값이 있을 수도 있고, 없을 수도 있는 컨텍스트를 제공합니다. Kotlin에서는 메이비 모나드를 직접 구현하거나, 옵셔널과 비슷한 개념을 사용하여 이를 모방할 수 있습니다.

class A2(val b: Maybe)
class B2(val c: Maybe)
class C2(val d: Maybe)
class D2(val value: Maybe)

fun getValueOfD2(a: A2): Maybe =
    a.b.flatMap { it.c }
       .flatMap { it.d }
       .flatMap { it.value } as Maybe<String>

코틀린의 안전한 널 처리와 비교

코틀린은 널 안전 호출(?.)과 엘비스 연산자(?:)를 통해 널을 처리하는 간편한 방법을 제공합니다. 위 예시는 단순히 메이비 모나드를 저런식으로 활용 가능하다는 것을 보여주기 위함 입니다.

class A3(val b: B3?)
class B3(val c: C3?)
class C3(val d: D3?)
class D3(val value: String?)

fun main(args: Array) {
    val a = A3(B3(C3(D3("someValue"))))
    println(a.b?.c?.d?.value ?: "") // "someValue" 출력
}

실습: 널이 아닌 프로퍼티 처리

메이비 모나드를 사용하면 널이 될 수 있는 프로퍼티뿐만 아니라, 널이 될 수 없는 프로퍼티를 가진 중첩된 클래스 구조에서도 유연하게 값을 처리할 수 있습니다.

class A4(val b: Maybe)
class B4(val c: Maybe)
class C4(val d: D4)
class D4(val value: Maybe)

//fmap을 활용하여 컨텍스트 변환
fun getValueOfD4(a: A4): Maybe =
    a.b.flatMap { it.c }
       .fmap { it.d }
       .flatMap { it.value }

메이비 모나드는 널 처리를 넘어서 다양한 종류의 실패를 안전하게 처리하는 강력한 도구입니다. 코틀린에서 이를 직접 구현하거나, 널 안전 호출과 같은 기능을 사용하여 비슷한 효과를 낼 수 있습니다. 함수형 프로그래밍 기법을 활용하면 코드를 더 간결하고 안전하게 만들 수 있습니다.

class A4 ( val b : Maybe ) class B4 ( val c : Maybe ) class C4 ( val d : D4 ) class D4 ( val value : Maybe ) // fmap 함수 getValueOfD4 ( a : A4 ): Maybe = abFlatMap { it.c } .fmap { it.d } . flatMap { it.value }
 
반응형