zio/zio-prelude
Functional Effect 의 다양한 합성(composition)
wefree
2021. 10. 10. 22:49
문제
아래와 같이 A, B, C, D 를 정의할 때, 다양하게 합성해 보자.
def A: Option[String] = {
println("A")
Some("A")
}
def B: Option[Int] = {
println("B")
Some(1)
}
def C: Option[String] = {
println("C")
None
}
def D: Option[Int] = {
println("D")
None
}
코드
Monad 합성
val monad: Option[(String, String)] = for {
c <- C
a <- A
} yield (a, c)
println(monad)
// C
// None
flatMap 에 의한 순차적인 합성은 fast fail 이므로 'A' 는 출력되지 않는다.
AssociativeBoth 합성 (Applicative)
import zio.prelude._
val ap: Option[(String, String)] = (C, A).mapN {
case (c, a) => (c, a)
}
println(ap)
// C
// A
// None
C 가 None 이지만 이후의 A 까지 모두 실행된다. 하나라도 None 이 있으면 최종 결과는 None 이다.
위의 코드는 아래처럼 작성할 수도 있다.
import zio.prelude._
val ap: Option[(String, String)] = C <*> A
println(ap)
// C
// A
// None
Validation 합성
import zio.prelude._
def cValid: Validation[String, String] = Validation.fromOption(C).mapError(_ => "C is empty")
def aValid: Validation[String, String] = Validation.fromOption(A).mapError(_ => "A is empty")
val validation: Validation[String, (String, String)] = Validation.validate(cValid, aValid)
println(validation)
// C
// A
// Failure(Chunk(),NonEmptyChunk(C is empty))
AssociativeBoth 처럼 모든 항목이 실행되면서, None 이 하나라도 있을 경우 해당 에러가 NonEmptyChunk 로 누적되어 기록된다.
import zio.prelude._
def cValid: Validation[String, String] = Validation.fromOption(C).mapError(_ => "C is empty")
def aValid: Validation[String, String] = Validation.fromOption(A).mapError(_ => "A is empty")
def dValid: Validation[String, Int] = Validation.fromOption(D).mapError(_ => "D is empty")
val validation: Validation[String, (String, String, Int)] = Validation.validate(cValid, aValid, dValid)
println(validation)
// C
// A
// D
// Failure(Chunk(),NonEmptyChunk(C is empty, D is empty))
AssociativeEither 합성
import zio.prelude._
val orElse: Option[Either[String, Int]] = A <+> B
println(orElse)
// A
// Some(Left(A))
import zio.prelude._
val orElse: Option[Either[String, String]] = C <+> A
println(orElse)
// C
// A
// Some(Right(A))
import zio.prelude._
val orElse: Option[Either[String, Int]] = C <+> D
println(orElse)
// C
// D
// None
Boolean 의 OR 연산자 처럼 왼쪽이 success 이면 오른쪽 계산은 skip 한다.