Home 함수형 프로그래밍과 모나드
Post
Cancel

함수형 프로그래밍과 모나드

책 정보

  • 코틀린으로 배우는 함수형 프로그래밍
  • yes24

0. 시작하기 전에

  • 모나드는 함수형 프로그래밍을 돕는 도구이다.
  • 모나드 인터페이스 자체는 간단하나, 왜 얘가 함수형프로그래밍을 돕는지 이해를 하면 좋은 것 같다. 해야한다는 아님
  • 모나드 법칙을 이해 안해도 함수형 프로그래밍 잘만 할 수 있을 것 같고, 새로운 모나드도 정의만 맞춘다면 구현할 수 있을 것 같다.
  • map 과 flatMap 을 지원하면 된다.
  • 현업에서 모나드를 우리가 구현해야 할 일은 사실 거의 없어보이긴한데, 기회가 되면 구현해보자.

1. 모나드 타입 클래스

1
2
3
4
5
6
interface Monad<out A> : Functor<A> {
    fun <V> pure(value: V): Monad<V>
    override fun <B> fmap(f: (A) -> B): Monad<B> = flatMap { a -> pure(f(a)) }
    infix fun <B> flatMap(f: (A) -> Monad<B>): Monad<B>
    infix fun <B> leadTo(m: Monad<B>): Monad<B> = flatMap { m }
}
  • Webflux Publisher 기준으로 생각해보면
    • pure = of, just 같은거라고 보면 되는것 같다.
    • fmap = map
    • flatMap = flatMap. infix function 으로 사용가능
    • leadTo = then. infix function 으로 사용가능

2. 메이비 모나드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sealed class Maybe<out A> : Monad<A> {

    companion object {
        fun <V> pure(value: V) : Maybe<V> = Just(0).pure(value)
    }

    override fun <V> pure(value: V): Maybe<V> = Just(value)

    override fun <B> fmap(f: (A) -> B): Maybe<B> = super.fmap(f) as Maybe<B>

    override infix fun <B> flatMap(f: (A) -> Monad<B>): Maybe<B> = when (this) {
        is Just -> try { f(value) as Maybe<B> } catch (e: ClassCastException) { Nothing }
        is Nothing -> Nothing
    }
}

3. 모나드 법칙

  1. 왼쪽 항등 법 : pure(x) flatMap f = f(x)
  2. 오른쪽 항등 법칙 : m flatMap pure = m
  3. 결합 법칙 : (m flatMap f) flatMap g = m flatMap { x -> f(x) flatMap g }
  4. 함수 합성 관점에서 모나드 법칙
1
2
3
identity compose f = f
f compose identity = f
(f compose g) compose h = f compose (g compose h)

4. IO 모나드

  • 함수형에서는 IO 기능이 아주 골칫거리임. DB조회, 호출, 파일읽기쓰기
  • 그냥 분리하고 격리해라
  • 하스켈 예시는 스킵

5. 리스트 모나드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sealed class FunList<out T> {
    companion object
}

object Nil : FunList<kotlin.Nothing>() {
    override fun toString(): String = "[]"
}

data class Cons<out T>(val head: T, val tail: FunList<T>) : FunList<T>() {
    override fun toString(): String = "[${foldLeft("") { acc, x -> "$acc, $x" }.drop(2)}]"
}

infix fun <T, R> FunList<T>.flatMap(f: (T) -> FunList<R>): FunList<R> = fmap(f).flatten()

infix fun <T, R> FunList<T>.fmap(f: (T) -> R): FunList<R> = when (this) {
  is Nil -> Nil
  is Cons -> Cons(f(head), tail.fmap(f))
}

나머지 뒷장

  • 로깅
    • 함수 중간에 로깅 -> 별루 -> 확장함수 withLog 붙이면? 부수효과.. 순수함수 오염시킨다
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    private fun functionalSolution2(list: FunStream<Int>) = list
      .fmap { addFive(it) withLog "$it + 5" }
      .fmap { square(it) withLog "$it * $it" }
      .fmap { isGreaterThan50(it) withLog "$it > 50" }
    
    private infix fun <T> T.withLog(log: String): T {
      println(log)
      return this
    }
    
    • WriterMonad 이용하자
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    data class WriterMonad<out T>(val value: T, val logs: FunStream<String>) : Monad<T> {
    
      companion object {
          fun <V> pure(value: V) = WriterMonad(0, mempty()).pure(value)
      }
    
      override fun <V> pure(value: V): WriterMonad<V> = WriterMonad(value, mempty())
    
      override fun <R> flatMap(f: (T) -> Monad<R>): WriterMonad<R> {
          val applied = f(this.value) as WriterMonad<R>
          return WriterMonad(applied.value, this.logs mappend applied.logs)
      }
    }
    
  • 예외처리
    • 메이비 모나드를 쓰면 사실 해결이 됨. Optional.empty()
    • 이더 모나드로 사유 기록 가능
    • 트라이 모나드 확장 가능.
  • 테스팅
    • 우선 테스트하기 좋은 순수함수를 만들어라. kotlintest 쓰면 알아서 string 만들어주기도 함.
      • FunList 같이 직접 만든 객체 랜덤은 생성못하니, generator 를 만들어서 넣어줘야함
    • 근데 우린 junit 쓸 예정.
    1
    2
    3
    4
    5
    6
    7
    
      class FunListTest: StringSpec({
          "testReverse" {
              forAll { a: String ->
                  reverse(reverse(a)) == a
              }
          }
      })
    
  • 디버깅
    • 보통 고차함수 + 게으른 실행이라.. 그냥 람다 함수 중간에 break point 찍어라

함수형 프로그래밍 마무리

1장 내용인데 까먹었으니깐

  • 불변성, 참조 투명성, 일급함수, 게으른 실행, 타입 안정성
  • 순수 함수
    • 동일 입력 동일 출력
    • 부수효과가 없다.
      • 예외발생도 부수효과
      • 객체 상태 변경
      • 공유 변수 수정
      • db 조회
  • 그럼 함수형 프로그래밍은 db 조회하지마?
    • 격리를 해서 최대한 순수함수를 만들고, 부수효과 일으키는 애는 격리를 하자
This post is licensed under CC BY 4.0 by the author.

인프런 스프링 Spring 핵심 원리 (기본)

자주 변하는 서비스를 만들며 구체적인 팁들