这是我之前quesion的后续内容。我再次阅读post以了解那里描述的设计。
他们引入了与Haxl类似的Job
monad。 Job[T]
是一个数据获取操作,它获取类型为T
的数据(可能包含其他操作,即它是一个数据提取序列)。
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)
}
他们还向实施执行execute
操作并返回未来的函数Job[T]
。
def execute[T](job: Job[T])(implicit ec: ExecutionContext): Future[T] = { ... }
对于并发数据提取,他们添加了新的PairJob
和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)]
现在他们可以写:
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)}
我的问题是如何将Job[T]
定义为Applicative
来编写代码,如上例所示。
答案 0 :(得分:2)
你很快就拥有了PairJob,你有适合你需要的东西。
使用任何泛型类型G(此处为Job)
如果你有配对:
def pair[A,B](ga: G[A], gb: G[B]): G[(A,B)]
(适用于Job,只是PairJob(ga, gb)
)
并且还可以映射,然后您可以轻松实现应用
def ap[A, B](ga: ⇒ G[A])(f: ⇒ G[A ⇒ B]): G[B] = {
val argAndFunc : G[(A, A => B)] = pair(ga, f)
argAndFunc map {case (arg, func) => func(arg)}
}
反之亦然,如果你有ap和map,你很容易得到对
def pair[A,B](ga: G[A], gb: G[B]): G[(A,B)] = {
val pairWithB : G[B => (A,B]) = ga map {a => b: B => (a,b)}
ap(gb)(pairWithB)
}
答案 1 :(得分:1)
你总是可以用flatMap和pure来定义地图:
def map[A,B](fa: Job[A])(f: A=>B): Job[B] = fa flatMap (f andThen pure)
然后你可以用map和flatMap来定义ap:
def ap[A,B](fa: => Job[A])(f: => Job[A => B]): Job[B] =
fa flatMap { a =>
f map (_(a))
}
或者用于理解糖:
def ap[A,B](fa: => Job[A])(f: => Job[A => B]): Job[B] =
for {
a <- fa
ff <- f
} yield ff(a)
或者你可以跳过地图:
def ap[A,B](fa: => Job[A])(f: => Job[A => B]): Job[B] =
fa flatMap { a =>
f flatMap { ff => pure(ff(a)) }
}