매우 조잡하고 간단하게 monad에 대해 부연설명을 한번 드려보겠습니다.
함수형 프로그래밍은 함수가 일등 시민(?)이고 코드의 조작이 주로 함수단위의 조합과 분리로 행해집니다.
이 방식으로 프로그래밍을 하고자 할 때 다음과 같은 문제가 생깁니다.
scala에서 일반적인 함수는 다음과 같이 조합하면 됩니다.
def f : Int => Boolean =
(a:Int) => if (a > 0) true else false
def g : Boolean => String =
(b:Boolean) => if (b) "Good" else "Bad"
// f와 g 함수의 조합 g ∘ f
def h(c:Int) : String = g(f(c))
// 또는
def h : Int => String = f andThen g
하지만 값을 입력받고 결과를 추가적인 context에 에워싸서 값을 돌려주는,
예들들어 Int값을 받고 Option[Int], List[Int], Future[Int]등을 결과값으로 리턴하는 함수들은
andThen으로 조합이 되지않는 문제가 발생합니다.
아래의 fOpt와 gOpt 조합 함수인 gOpt ∘ fOpt는 어떻게 조합 해야 할까요?
def fOpt : Int => Option[Boolean] =
(a:Int) => if (a > 0) Some(true) else if (a < 0) Some(false) else None
def gOpt : Boolean => Option[String] =
(b:Boolean) => if (b) Some("Good") else Some("Bad")
// fOpt와 gOpt 함수의 조합 gOpt ∘ fOpt
def hOpt : Int => Option[String] = fOpt(_) ??? gOpt
고려할 요소를 생각해보면, fOpt의 결과값이 Some(x)인가 None인가에 따라
gOpt의 적용 여부가 달라지는 로직이 존재해야 됩니다.
이러한 로직은 context에 둘러싸인 값을 다루는데에서 반복적으로 나타납니다.
이런 종류의 로직을 추상화시켜 단순화시킬 수 있는 어떤 수단이 있을까요?
그 수단이 바로 Monad입니다. 다시 말해서, 선행 함수의 결과값context와 후속 함수의 입력값과의
연관성을 추상화 시킨 인터페이스 (java의 인터페이스가 아닌 일반적 의미에서) 입니다.
이것을 이용해서 추가 context를 가진 함수도 일반 함수처럼 조합할 수 있게 되는것이지요.
scala에서는 이러한 monadic 로직이 flatMap을 통해 기본으로 구현 되어 있습니다.
object Option의 flatMap 소스코드를 보시면 실제로 하는일이 None인지를 판단해서
인자로 받은 함수를 적용 할지를 판단해서 값을 리턴하는것을 볼 수 있습니다.
// fOpt와 gOpt 함수의 조합 gOpt ∘ fOpt
def hOpt = fOpt(_) flatMap gOpt
// Option object의 flatMap 소스 코드 내부
object Option {
def flatMap[B](f: A => Option[B]): Option[B] =
if (isEmpty) None else f(this.get)
}