This is a follow-up to my previous quesion. I am reading this post again to understand the design described there.
They introduce a Job
monad similar to Haxl. Job[T]
is a data fetch operation that fetches data of type T
(and may consist of other operations, i.e. it is a data fetches sequence).
sealed trait Job[+T]
case class PureJob[T](value: T) extends Job[T]
case class BlockedJob[S,T](f: S => Job[T], job: Job[S]) extends Job[T]
case class FetchJob[T](url: Url) extends Job[T]
def pure[T](value: T): Job[T] = PureJob(value)
def flatMap[S,T](f: S => Job[T], job: Job[S]): Job[T] =
job match {
case PureJob(value) => f(value)
case _ => BlockedJob(f, job)
}
They introduce also a function execute
to actually execute a Job[T]
operation and return a future.
def execute[T](job: Job[T])(implicit ec: ExecutionContext): Future[T] = { ... }
For concurrent data fetching they add new PairJob
, and MapJob
:
case class MapJob[S, T](f: S => T, job: Job[S]) extends Job[T]
case class PairJob[S, T](jobS: Job[S], jobT: Job[T]) extends Job[(S, T)]
Now they can write:
val jobA: FetchJob[A] = ...
val jobB: FetchJob[B] = ...
val f: A => B = ...
// jobAB is a MapJob of "f" and PairJob of jobA and jobB
val jobAB = (jobA |@| jobB) {(a, b) => f(a, b)}
My question is how to define Job[T]
as Applicative
to write code as in the example above.