scala/basic

Scala State Monad

wefree 2021. 8. 3. 12:12

문제

State Monad 를 만들고, 이를 사용해 다음을 계산하라.

골프 공을 쳐서 처음에는 20m, 두번째는 10m, 세번째는 15m 를 보냈다.

그런데 시작라인에서 3m 를 더 나온 위치에 공을 두고 쳤다면 최종 골프공은 시작라인에서 어느정도 떨어져 있을까?

3 + 20 + 10 + 15 = 48

 

코드

case class State[S, A](run: S => (S, A)) {
  def flatMap[B](f: A => State[S, B]): State[S, B] = State { s0 =>
    val (s, a) = run(s0) // 현재 상태의 run 을 적용하고
    f(a).run(s) // 이후에 f 를 적용해 S => (S, A) 함수를 갱신함
  } // 기존 run 과는 다른 새롭게 정의된 S => (S, A) 함수를 이용하는 State 가 만들어짐

  def map[B](f: A => B): State[S, B] = flatMap(a => State.lift(f(a)))
}

object State {
  // 초기 결과값으로 value 로 부터 State 를 만듬
  // State 를 만든다는 것은 S => (S, A) 를 정의하는 것인데, A 값으로 value 사용
  def lift[S, A](value: A): State[S, A] = State(s => (s, value))
}

object GolfDistance {
  // 이동 거리를 계속 더해야 하기 때문에 state, result 를 모두 s + n 으로
  def swing(n: Int): State[Int, Int] = State(s => (s + n, s + n))

  def main(args: Array[String]): Unit = {
    val distance: State[Int, Int] = for {
      _     <- swing(20)
      _     <- swing(10)
      total <- swing(15) // 최종 state 와 result 에만 관심이 있다!
    } yield total

    val (s, a) = distance.run(3) // 3m 앞에서 시작해서 초기값을 3으로 해서 실행함
    println(a)
  }
}

 

설명

State Monad 를 직접 구현해 사용했지만, ZIO prelude 의 State Monad 에서 처럼 zio-prelude 라이브러리를 이용할 수도 있다. zio-prelude ZPure 를 사용하더라도 코드가 비슷함을 확인 할 수 있다.

 

그런데 java.util.concurrent._ 를 이용해 아래처럼 OOP 스타일로도 구현할 수 있지 않을까?

import java.util.concurrent.atomic.AtomicInteger

object GolfDistance {
  case class State(s: AtomicInteger) {
    def swing(n: Int): Int = s.addAndGet(n)
    def get: Int           = s.get()
  }

  def main(args: Array[String]): Unit = {
    val distance = State(new AtomicInteger(3))
    distance.swing(20)
    distance.swing(10)
    distance.swing(15)

    val total = distance.get
    println(total)
  }
}

위의 구현이 더 이해하기 쉬워 좋아보이는데, State Monad 로 구현했을 때 장점은 무엇인가?

그것은 아래 코드 때문인 것 같다. 

def swing(n: Int): Int = s.addAndGet(n)

현재 요구 사항에 맞는 addAndGet 이 있어서 위와 같이 구현했지만, 더 복잡한 로직이 들어간다면 atomic 하게 처리할 수 있을까?

 

참고: https://alvinalexander.com/scala/functional-programming-simplified-book/