Skip to content

It executes liftF effect twice in a product composition #912

@TalkingFoxMid

Description

@TalkingFoxMid

I've found that this piece of code prints "message" twice:

object Main extends App {
  object StringByString extends Data[String, String] {
    type StringByString[F[_]] = DataSource[F, String, String]
    def name: String = "stringByString"

    def source[F[_]: Concurrent]: StringByString[F] = new StringByString[F] {
      override def CF: Concurrent[F] = Concurrent[F]

      override def data: Data[String, String] = StringByString

      override def fetch(accountId: String): F[Option[String]] =
        Option(accountId).pure[F]

    }

  }
  val ds = StringByString.source[IO]
  
  val f = Fetch.optional(
    "string", ds
  ).product(Fetch.liftF(IO(println("message"))))

  println(Fetch.run(f).unsafeRunSync())

}

When a BlockedRequest is followed by Fetch.liftF, interpreter will execute that effect twice.

It is not very pleasant for us because in that Fetch.liftF we do a bunch of computational efforts and duplicating these computations might be quite wasteful.

Possible solution:

The problem I guess is in that piece of code:

override def product[A, B](fa: Fetch[F, A], fb: Fetch[F, B]): Fetch[F, (A, B)] =
        Unfetch[F, (A, B)](for {
          fab <- (fa.run, fb.run).tupled
          result = fab match {
            case (Throw(e), _) =>
              Throw[F, (A, B)](e)
            case (Done(a), Done(b)) =>
              Done[F, (A, B)]((a, b))
            case (Done(a), Blocked(br, c)) =>
              Blocked[F, (A, B)](br, product(fa, c))
            case (Blocked(br, c), Done(b)) =>
              Blocked[F, (A, B)](br, product(c, fb))
            case (Blocked(br, c), Blocked(br2, c2)) =>
              Blocked[F, (A, B)](combineRequestMaps(br, br2), product(c, c2))
            case (_, Throw(e)) =>
              Throw[F, (A, B)](e)
          }
        } yield result)

Here we execute fb (which is Fetch.liftF) twice:

  1. (fa.run, fb.run).tupled
  2. product(c, fb)

We could reuse the result of fb by replacing product(c, fb) to c.map((_, b))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions