【发布时间】:2012-01-17 15:17:17
【问题描述】:
谁能解释一下 Scala 上下文中 Functor 和 Monad 之间的区别?
【问题讨论】:
-
这提供了一个很好的指南:gabrielsw.blogspot.com/2011/08/…
-
@LuigiPlinge:您应该将其发布为答案。
标签: scala functional-programming
谁能解释一下 Scala 上下文中 Functor 和 Monad 之间的区别?
【问题讨论】:
标签: scala functional-programming
不久前我写过:http://gabrielsw.blogspot.com/2011/08/functors-applicative-functors-and.html(虽然我不是专家)
首先要了解的是类型 'T[X]':它是一种“上下文”(对类型进行编码很有用,这样您就可以“组合”它们)但是请参阅其他答案: )
好的,现在您将类型放在上下文中,例如 M[A](A“在”M 中),并且您有一个普通函数 f:A=>B ...您不能继续应用它,因为函数需要 A 而你有 M[A]。您需要一些方法来“解包” M 的内容,应用该功能并再次“打包”它。如果你对 M 的内部有“深入”的了解,你就可以做到,如果你将它概括为一个特征,你会以
结尾trait Functor[T[_]]{
def fmap[A,B](f:A=>B)(ta:T[A]):T[B]
}
而这正是函子。它通过应用函数 f 将 T[A] 转换为 T[B]。
Monad 是一种神秘的生物,具有难以理解和多种隐喻,但我发现一旦获得应用函子就很容易理解:
Functor 允许我们将函数应用于上下文中的事物。但是如果我们想要应用的功能已经在一个上下文中呢? (如果您的函数采用多个参数,则很容易在这种情况下结束)。
现在我们需要像 Functor 这样的东西,但它也需要上下文中已经存在的函数并将它们应用于上下文中的元素。这就是应用函子。这是签名:
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]
}
到目前为止一切顺利。 现在出现了 monad:如果现在您有一个将事物置于上下文中的函数怎么办?它的签名将是 g:X=>M[X] ...你不能使用仿函数,因为它需要 X=>Y 所以我们将以 M[M[X]] 结尾,你不能使用applicative functor 因为期望该函数已经在上下文 M[X=>Y] 中。
所以我们使用了一个 monad,它接受一个函数 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]
编辑:忘记绑定它的重要事项:>>= 符号称为 bind,在 Scala 中是 flatMap。 (另外,附带说明一下,函子、应用程序和 monad 必须遵循一些法则才能正常工作)。
【讨论】:
我认为这篇精彩的博文将首先帮助您 monad。 http://blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/
【讨论】:
以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 等。它们都是函子
我觉得有趣的是,您可能会立即跳到(不正确的)结论,即 Functor 是As 的“容器”。这是一个不必要的限制。例如,考虑一个函数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 中,functor 被设计为一个 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 中,monad 是这样设计的:
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 的 Monads are not Metaphors 非常清楚地描述了为什么会这样,以及 Tony Morris 在 configuration via the reader monad 上的东西,这是一个很好的实际示例,可以说明在 monad 中编写程序可能意味着什么。
【讨论】:
Scala 本身并没有那么强调Functor 和Monad 术语。我猜使用map 是仿函数端,使用flatMap 是Monad 端。
对我来说,寻找和玩弄scalaz 是迄今为止在 scala 上下文(相对于 haskell 上下文)中了解这些功能概念的最佳途径。两年前,当我开始使用 scala 时,scalaz 代码对我来说是乱码,然后几个月前我又开始寻找它,我意识到它确实是这种特殊风格的函数式编程的干净实现。
例如,Monad 实现表明 monad 是指向 functor,因为它扩展了 Pointed 特征(以及 Applicative 特征)。我邀请你去看看代码。它在源本身中具有链接,并且很容易按照链接进行操作。
所以函子更通用。 Monads 提供了额外的特性。要了解当你有一个函子或当你有一个单子时你可以做什么,你可以看看MA
您会看到需要隐式仿函数(尤其是应用仿函数)的实用方法,例如 sequence,有时还会看到需要完整 monad 的方法,例如 replicateM。
【讨论】:
详细阐述这两个概念的最佳文章是来自Eric Torreborre's Blog 的“The Essence of the Iterator Pattern ”。
函子
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 怎么样?除了成为Applicative Functor 之外,您还需要bind 或join,才能成为Monad。我猜你可能知道bind 和flatMap。
trait Bind[Z[_]] { def bind[A, B](a: Z[A], f: A => Z[B]): Z[B] }。更详细地说,在Apply 之后定义Bind:github.com/retronym/scalaz7-experimental/blob/master/core/src/…
Functor、Pointed、Bind、Monad)使用类型 members 而不是类型 parameters :gist.github.com/1458981。见permalink.gmane.org/gmane.comp.lang.scala/25185