请问有人能解释一下Scala环境下Functor和Monad之间的区别吗?
请问有人能解释一下Scala环境下Functor和Monad之间的区别吗?
Scala本身并不太强调Functor和Monad这些术语。我想使用map是表示Functor方面,使用flatMap是表示Monad方面。
对我来说,研究和探索scalaz迄今为止是了解scala上这些函数概念最好的途径(与haskell上下文相比)。两年前我开始学习scala时,scalaz代码对我来说就像天书一样,然后几个月前我重新开始看,发现它真的是该特定风格函数编程的干净实现。
例如,Monad
实现显示monad是一个指向functor的点,因为它扩展了 trait(以及 trait) 。我邀请您去查看代码。它在源代码中有链接,非常容易跟踪。
因此,Functor更普遍。Monads提供附加功能。要了解当您拥有functor或monad时可以做什么,请查看MA
。
您将看到需要隐式functor(特别是applicative functors)的实用程序方法,例如sequence和有时需要完整monad的方法,例如replicateM。
以scalaz为参考点,一个类型F[_]
(即,一个由某个单一类型参数化的类型F)如果可以提升函数,则它是一个函子。这是什么意思:
class Function1W[A, B](self: A => B) {
def lift[F[_]: Functor]: F[A] => F[B]
}
也就是说,假设我有一个函数A => B
,一个函子F[_]
,那么现在我就有了一个函数F[A] => F[B]
。实际上这只是从相反的角度来看待scala的map
方法,忽略了CanBuildFrom
的部分,它基本上是这样的:
F[A] => (A => B) => F[B]
如果我有一个字符串列表,以及从字符串到整数的函数,那么我显然可以生成一个整数列表。这适用于Option、Stream等所有函子。
我觉得有趣的是,你可能会马上跳进(错误的)结论,认为函子是包含 A
的“容器”。这是一种不必要的限制。例如,考虑一个函数 X => A
。 如果我有一个函数 X => A
和一个函数 A => B
,那么显然,通过组合,我就有了一个函数 X => B
。但现在,请这样看待它:
type F[Y] = X => Y //F is fixed in X
(X => A) andThen (A => B) is X => B
F[A] A => B F[B]
因此,对于某个固定的X,类型X => A也是一个函子。在scalaz中,函子被设计为以下特征(trait):
trait Functor[F[_]] { def fmap[A, B](fa: F[A], f: A => B): F[B] }
因此,上面的Function1.lift
方法被实现。def lift[F[_]: Functor]: F[A] => F[B]
= (f: F[A]) => implicitly[Functor[F]].fmap(f, self)
一些函数对象实例:
implicit val OptionFunctor = new Functor[Option] {
def fmap[A, B](fa: Option[A], f: A => B) = fa map f
}
implicit def Functor1Functor[X] = new Functor[({type l[a]=X => a})#l] {
def fmap[A, B](fa: X => B, f: A => B) = f compose fa
}
在scalaz中,单子的设计如下:trait Monad[M[_]] {
def pure[A](a: A): M[A] //given a value, you can lift it into the monad
def bind[A, B](ma: M[A], f: A => B): M[B]
}
这可能并不特别明显有什么用,但事实证明它非常有用。我发现Daniel Spiewak的《单子不是隐喻》非常清晰地解释了为什么会这样,并且Tony Morris在通过reader单子进行配置方面的东西,是关于在单子中编写程序所指的内容的很好的实际示例。
trait Functor[T[_]]{
def fmap[A,B](f:A=>B)(ta:T[A]):T[B]
}
trait Applicative[T[_]] extends Functor[T]{
def pure[A](a:A):T[A]
def <*>[A,B](tf:T[A=>B])(ta:T[A]):T[B]
}
因此,我们使用一个单子,它接受一个函数X=>M[X]和已经在上下文M[A]中的东西,并将函数应用于上下文中的内容,将结果打包在一个上下文中。签名如下:
trait Monad[M[_]] extends Applicative[M]{
def >>=[A,B](ma:M[A])(f:A=>M[B]):M[B]
}
这可能有点抽象,但如果你思考如何使用"Option"来组合函数X=>Option[X],就能看到它的实际应用。
补充说明:重要的一点是将它们联系起来的符号>>=在Scala中被称为bind,也被称为flatMap。(此外,值得一提的是,有一些法则可以让函子、应用函子和单子正常工作)。
详细解释这两个概念的最佳文章是来自Eric Torreborre's Blog的"The Essence of the Iterator Pattern"。
Functor
trait Functor[F[_]] {
def fmap[A, B](f: A => B): F[A] => F[B]
}
- 解释Functor的一种方式是将其描述为类型A的值的计算。
例如:
List[A]
是返回多个类型A值的计算(非确定性计算),Option[A]
用于你可能有或可能没有的计算,Future[A]
是一个稍后获取类型A值的计算,等等。- 另一种形象的理解方式是,它是类型A值的“某种容器”。
这是定义以下内容的基本层:
PointedFunctor
(创建类型F[A]
的值)和Applic
(提供方法applic
,是容器F(F[A => B])
中的计算值,适用于值F[A]
),Applicative Functor
(将Applic
和PointedFunctor
聚合在一起)。这三个元素被用于定义Monad
。
bind
怎么样?为了成为Monad
,除了成为一个Applicative Functor
之外,您还需要使用bind
或join
。您可能知道bind
是flatMap
。 - Dan Burton。更详细地,在定义了
Apply之后定义
Bind`:https://github.com/retronym/scalaz7-experimental/blob/master/core/src/main/scala/scalaz/Bind.scala - VonCFunctor
,Pointed
,Bind
,Monad
):https://gist.github.com/1458981。请参阅http://permalink.gmane.org/gmane.comp.lang.scala/25185。 - VonC