在这种情况下如何定义应用程序?

How to define Applicative in this case?

这是我之前 quesion. I am reading this post 的后续,再次了解那里描述的设计。

他们引入了一个类似于 HaxlJob 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] = { ... }

对于并发数据获取,他们添加了新的PairJobMapJob

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)) }
  }