在这种情况下如何定义应用程序?
How to define Applicative in this case?
这是我之前 quesion. I am reading this 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]
操作和 return 一个未来。
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
来编写上面例子中的代码。
很快你就拥有了 PairJob,你拥有了应用程序所需的一切。
对于任何泛型类型 G,(这里是 Job)
如果你有配对:
def pair[A,B](ga: G[A], gb: G[B]): G[(A,B)]
(对于 Job,只是 PairJob(ga, gb)
)
还有map,那么就可以轻松实现apply
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,就很容易得到pair
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)
}
你总是可以用 flatMap 和 pure 来定义 map:
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)) }
}
这是我之前 quesion. I am reading this 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]
操作和 return 一个未来。
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
来编写上面例子中的代码。
很快你就拥有了 PairJob,你拥有了应用程序所需的一切。
对于任何泛型类型 G,(这里是 Job)
如果你有配对:
def pair[A,B](ga: G[A], gb: G[B]): G[(A,B)]
(对于 Job,只是 PairJob(ga, gb)
)
还有map,那么就可以轻松实现apply
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,就很容易得到pair
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)
}
你总是可以用 flatMap 和 pure 来定义 map:
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)) }
}