Scala Functor和Monad的差异

时间:2011-12-10 23:01:31

标签: scala functional-programming

有人可以解释一下Scala上下文中Functor和Monad之间的区别吗?

5 个答案:

答案 0 :(得分:48)

Scala本身并没有那么强调FunctorMonad这些术语。我想使用map是仿函数方面,使用flatMap是Monad方。

对于我来说,查看和使用scalaz一直是最好的途径,以了解scala上下文中的那些功能概念(与haskell上下文相比)。两年前,当我开始scala时,scalaz代码对我来说是胡言乱语,然后几个月前我开始重新审视,我意识到它真的是一种干净的函数编程风格的实现。

例如,Monad实现显示monad是一个尖头functor,因为它扩展了Pointed特征(以及Applicative特征)。我邀请你去看看代码。它链接在源本身,并且很容易跟踪链接。

所以仿函数更通用。 Monads提供额外的功能。为了了解当你有一个仿函数或你有一个monad时你可以做什么,你可以看一下MA

您将看到需要隐式仿函数(特别是应用仿函数)的实用程序方法,例如sequence以及需要完整monad的方法,例如replicateM

答案 1 :(得分:28)

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]

如果我有一个字符串列表,一个从String到Int的函数,那么我显然可以生成一个Ints列表。这适用于Option,Stream等。它们都是仿函数

我觉得有趣的是,你可能会立即跳到(错误的)结论,即Functor是一个"容器" 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也是一个仿函数。在 scalaz 中,仿函数被设计为如下特征:

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中编写你的程序

答案 2 :(得分:20)

前段时间我写过:http://gabrielsw.blogspot.com/2011/08/functors-applicative-functors-and.html(我不是专家)

要理解的第一件事是'T [X]'类型:它是一种“上下文”(对于类型中的事物进行编码很有用,并且你正在“编写”它们)但是请看其他答案: )

好的,现在你把你的类型放在一个上下文中,比如M [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 =&gt; M [X] ...你不能使用仿函数,因为它期望X =&gt; Y所以我们将以M [M [X]]结束,你不能使用applicative functor,因为期望函数已经在上下文M [X =&gt; Y]中。

所以我们使用一个monad,它接受一个函数X =&gt; M [X]和已经存在于上下文M [A]中的东西,并将该函数应用于上下文中的内容,将结果打包在一个上下文中。签名是:

trait Monad[M[_]] extends Applicative[M]{
  def >>=[A,B](ma:M[A])(f:A=>M[B]):M[B]
}

它可能非常抽象,但如果您考虑如何使用“选项”,它会告诉您如何编写函数X =&gt;选项[X]

编辑:忘记绑定它的重要事项:&gt;&gt; =符号在Scala中被称为 bind 并且是 flatMap 。 (另外,作为附注,有一些法律规定仿函数,应用程序和monad必须遵循才能正常工作)。

答案 3 :(得分:17)

详细列出这两个概念的最佳文章是来自The Essence of the Iterator Pattern 的“Eric Torreborre's Blog”。

<强>函子

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 FunctorApplicPointedFunctor的汇总)。

所有这三个元素都用于定义 Monad

答案 4 :(得分:0)

我认为这篇精彩的博文会首先为monad提供帮助。 http://blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/