我有以下适用的定义和实施:
import simulacrum._
@typeclass trait Applicative[F[_]] {
def pure[A](a: A): F[A]
def apply[A, B](fa: F[A])(ff: F[A => B]): F[B]
def map[A,B](fa: F[A])(f: A => B): F[B] =
apply(fa)(pure(f))
}
object Applicative {
implicit val optionApplicative: Applicative[Option] = new Applicative[Option] {
def pure[A](a: A): Option[A] = Some(a)
def apply[A, B](fa: Option[A])(ff: Option[A => B]): Option[B] = (fa, ff) match {
case (None, _) => None
case (Some(_), None) => None
case (Some(a), Some(f)) => Some(f(a))
}
}
implicit val listApplicative: Applicative[List] = new Applicative[List] {
def pure[A](a: A): List[A] = List(a)
def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] =
(fa zip ff).map { case (a, f) => f(a) }
}
}
当我在列表上执行地图时:
Applicative[List].map(List(1,2,3))(_ + 1)
我有:
res2: List[Int] = List(2)
但我确实期待List(2,3,4)
。我做错了什么?
答案 0 :(得分:4)
def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] =
(fa zip ff).map { case (a, f) => f(a) }
这是由ff
尺寸 1 def pure[A](a: A): List[A] = List(a)
引起的。
所以:
(fa zip ff).map { case (a, f) => f(a) }
=>:
List(1, 2, 3) zip List(f).map { case (a, f) => f(a) }
=>:
List((1, f)).map { case (a, f) => f(a) }
答案 1 :(得分:0)
要为@ chengpohi的答案添加解决方案,我想指出为Applicative
提供List
个实例的两种经典方式。
第一个是Zip
,你已经实现了,所以当你有一个值列表和一个函数列表时,它会逐个应用元素函数(第一个函数应用于第一个元素) , 等等)。从两个大小为n
和m
的列表开始,输出将是一个大小为min(n, m)
的列表。
第二个是Cartesian
,对于元素列表和函数列表,它将每个函数应用于每个元素。因此,如果您从大小为n
和m
的列表开始,您将获得一个大小为n × m
的列表。
以下是它的简单实现:
implicit val cartesian: Applicative[List] = new Applicative[List] {
def pure[A](a: A): List[A] = List(a)
def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] = for {
a <- fa
f <- ff
} yield f(a)
}
第二个更标准(它确实是从Monad
实例继承的那个)并且是cat和scalaz AFAIK中的默认值。
您必须要问自己的是,在两个名单(大小超过1
)上使用您的应用程序时,您有什么期望,以获得更好的想法。你在问题中说的话让我觉得你最好还是选择第二个。请记住,这两个是经典,但不是唯一可能的。如果您需要自定义实例,您可以定义它(请记住它应该遵守Applicative
法律。)