Scala中的Monad特质

26

如何以Scala的一种通用方式(例如Haskell中的Monad类型类)表示单子?是否有可能定义一个trait Monad来实现这个目的?

5个回答

46

你可以尝试像这样做:

trait Monad[+M[_]] {
  def unit[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}

// probably only works in Scala 2.8
implicit def monadicSyntax[M[_], A](m: M[A])(implicit tc: Monad[M]) = new {
  private val bind = tc.bind(m) _

  def map[B](f: A => B) = bind(f compose tc.unit)

  def flatMap[B](f: A => M[B]) = bind(f)
}

implicit object MonadicOption extends Monad[Option] {
  def unit[A](a: A) = Some(a)

  def bind[A, B](opt: Option[A])(f: A => Option[B]) = opt flatMap f
}
当然,你也可以为任何其他的单子定义类似的隐式对象。从Haskell的角度来看,你可以将Monad看作是一个类型类,而MonadicOption则是该类型类的一个特定实例。隐式转换monadicSyntax只是演示了如何使用该类型类,使得任何满足Monad类型类的东西都能够与Scala的for推导一起使用。
通常情况下,Scala标准库中实现flatMap的大多数内容都是单子。尽管Scala没有定义一个通用的Monad类型类(虽然这会非常有用),但它依赖于语法解析器的一种技巧,允许使用实现适当方法的任何东西来使用for推导。具体来说,这些方法包括mapflatMapfilter(或者在命令式形式中为foreachfilter)。

4
你真的不会用这个做任何实际的事情,是吗? :-) 说真的,我已经使用Scala一段时间了,发现许多Haskell中需要使用Monad类型类的情况在Scala中由于特质和子类型而不存在。尽管上面给出的类型类很酷,但在Scala中解决问题肯定不是惯用方法。 - Daniel Spiewak
1
但它们确实会出现。它们一直出现。每天有几次,我需要 liftA2sequenceA。尽管这些只需要 Applicative 而不是 Monad,但是如果在您的编程中没有出现这些内容,那么您一定是在编写非常简单的东西。 - Apocalisp
3
我可能只是没有仔细观察出现的情况。另一方面,我也可能正在设计我的代码,使其他解决方案更好。Scala允许使用面向对象和函数式编程的解决方案来解决需要在Haskell或ML等语言中进行大量函数式魔法的问题(例如使用开放递归而不是函数器技巧)。 - Daniel Spiewak
1
当然是真实的 - 想象一下解释器的单子表示!没有单子抽象简直太糟糕了! - Dario
Monad的概念不就是一个容器/上下文吗,其中“unit”应该将一个对象放入Monad的上下文中并返回。这里的“unit”方法返回的是普通的“Option”,而不是类似于“Monad [option]”之类的东西,对吗? - Dev Maha
显示剩余4条评论

15

你可能会觉得 scalaz 这个项目很有趣;它不仅实现了单子,还包含了许多其他的(函数式)内容。


4

4

http://www.codecommit.com/blog/ruby/monads-are-not-metaphors

这是一篇关于Monad模式及其在Scala中实现的有用而冗长的文章,作者是丹尼尔,他为这个问题撰写了被接受的答案。
(对于那些通过StackOverflow网站搜索的神秘方式偶然发现这个“古老”的问题的人。)

2
Scala通过使用隐式参数(特别是视图界定和上下文界定)实现了类似于Haskell的类型类的功能。您可以在Scala 2.8中看到这些东西的使用,例如OrderingNumeric等特征。
话虽如此,请看看Scalaz项目。它具有单子、函子、箭头等等...整个套餐。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接