如何定义 <*>对于 Option[List[_]] n Scala [英] How to define <*> for Option[List[_]] n Scala
问题描述
这是我之前的问题的后续,示例见于互联网.
This is a followup to my previous question with an example found on the Internet.
假设我定义了一个类型类 Applicative
如下:
Suppose I define a typeclass Applicative
as follows:
trait Functor[T[_]]{
def map[A,B](f:A=>B, ta:T[A]):T[B]
}
trait Applicative[T[_]] extends Functor[T] {
def unit[A](a:A):T[A]
def ap[A,B](tf:T[A=>B], ta:T[A]):T[B]
}
我可以为List
object AppList extends Applicative[List] {
def map[A,B](f:A=>B, as:List[A]) = as.map(f)
def unit[A](a: A) = List(a)
def ap[A,B](fs:List[A=>B], as:List[A]) = for(f <- fs; a <- as) yield f(a)
}
为了方便,我可以定义一个隐式转换
来添加一个方法<*>
到List[A=>B]
For convenience I can define an implicit conversion
to add a method <*>
to List[A=>B]
implicit def toApplicative[A, B](fs: List[A=>B]) = new {
def <*>(as: List[A]) = AppList.ap(fs, as)
}
现在我可以做一件很酷的事情了!
压缩两个列表 List[String]
并将 f2
应用到 applicative 风格的每一对
Now I can do a cool thing !
zip two lists List[String]
and apply f2
to every pair in applicative style
val f2: (String, String) => String = {(first, last) => s"$first $last"}
val firsts = List("a", "b", "c")
val lasts = List("x", "y", "z")
scala> AppList.unit(f2.curried) <*> firsts <*> lasts
res31: List[String] = List(a x, a y, a z, b x, b y, b z, c x, c y, c z)
到目前为止,很好,但现在我有:
So far, so good but now I have:
val firstsOpt = Some(firsts)
val lastsOpt = Some(lasts)
我想压缩 firsts
和 lasts
,应用 f2
,然后得到 Option[List[String]]代码> applicative 风格.换句话说,我需要
<*>
用于 Option[List[_]]
.我该怎么做?
I would like to zip firsts
and lasts
, apply f2
, and get Option[List[String]]
in applicative style. In other words I need <*>
for Option[List[_]]
. How can I do it ?
推荐答案
首先,你需要一个Option
的applicative实例:
Firstly, you need an instance of applicative for Option
:
implicit object AppOption extends Applicative[Option] {
def map[A, B](f: A => B, o: Option[A]) = o.map(f)
def unit[A](a: A): Option[A] = Some(a)
def ap[A, B](of: Option[A => B], oa: Option[A]) = of match {
case Some(f) => oa.map(f)
case None => None
}
}
然后你也可以为两个应用程序的组合创建一个应用程序实例(注意,基于 Haskell 版本):
Then you can also create an applicative instance for the composition of two applicatives (note, based on the Haskell version):
class AppComp[F[_], G[_]](fa: Applicative[F], ga: Applicative[G]) extends Applicative[({ type f[A] = F[G[A]]})#f] {
def map[A, B](f: A => B, a: F[G[A]]): F[G[B]] = fa.map((g: G[A]) => ga.map(f, g), a)
def unit[A](a: A) = fa.unit(ga.unit(a))
def ap[A, B](f: F[G[A => B]], a: F[G[A]]): F[G[B]] = {
val liftg: G[A => B] => (G[A] => G[B]) = gf => (gx => ga.ap(gf, gx))
val ffg: F[G[A] => G[B]] = fa.map(liftg, f)
fa.ap(ffg, a)
}
}
implicit def toComp[F[_], G[_]](implicit fa: Applicative[F], ga: Applicative[G]) = new AppComp[F, G](fa, ga)
现在你终于可以这样做了:
Finally you can now do:
val ola = toComp[Option, List]
ola.ap(ola.ap(ola.unit(f2.curried), firstsOpt), lastsOpt)
您也可以通过将 <*>
概括为适用于任何应用程序来消除一些噪音.
You could probably also remove some of the noise by generalising <*>
to work for any applicative.
这篇关于如何定义 <*>对于 Option[List[_]] n Scala的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!