ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Functional Effect 의 다양한 합성(composition)
    zio/zio-prelude 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 한다.

    'zio > zio-prelude' 카테고리의 다른 글

    ZIO prelude 의 Reader Monad  (0) 2022.07.24
    ZIO prelude 의 State Monad  (0) 2021.09.26
    NewTypes 과 Equal 을 활용한 객체 비교  (0) 2021.09.23

    댓글

Designed by Tistory.