为了在monad上下文中创建任何可操作的东西,如果使用Haskell - 我只是在任何地方添加给定类型的Monad类的实现。所以我根本没有触及数据类型定义的来源。喜欢(人为的东西)
data Z a = MyZLeft a | MyZRight a
swap (MyZLeft x) = MyZRight x
swap (MyZRight x) = MyZLeft x
instance Monad Z where
return a = MyZRight a
(>>=) x f = case x of
MyZLeft s -> swap (f s)
MyZRight s -> swap (f s)
所以我没有触及Z的定义,而是将其作为monad
如何在Scala中执行此操作?似乎除了混合一些特征和定义方法map / flatMap / filter / withFilter之外别无他法?
答案 0 :(得分:21)
看看scalaz
:
// You could use implementation in the end of this answer instead of this import
import scalaz._, Scalaz._
sealed trait Z[T]
case class MyZLeft[T](t: T) extends Z[T]
case class MyZRight[T](t: T) extends Z[T]
def swap[T](z: Z[T]) = z match {
case MyZLeft(t) => MyZRight(t)
case MyZRight(t) => MyZLeft(t)
}
implicit object ZIsMonad extends Monad[Z] {
def point[A](a: => A): Z[A] = MyZRight(a)
def bind[A, B](fa: Z[A])(f: A => Z[B]): Z[B] = fa match {
case MyZLeft(t) => swap(f(t))
case MyZRight(t) => swap(f(t))
}
}
用法:
val z = 1.point[Z]
// Z[Int] = MyZRight(1)
z map { _ + 2 }
// Z[Int] = MyZLeft(3)
z >>= { i => MyZLeft(i + "abc") }
// Z[String] = MyZRight(1abc)
z >>= { i => (i + "abc").point[Z] }
// Z[String] = MyZLeft(1abc)
for-comprehensions
(similar to do-notation):
for {
i <- z
j <- (i + 1).point[Z]
k = i + j
} yield i * j * k
// Z[Int] = MyZRight(6)
另请参阅Scalaz cheatsheet和Learning scalaz。
scalaz
中没有魔法 - 你可以在没有scalaz
的情况下实现这一点。
相关:Typeclases in Scala & Haskell。
如果您不想使用Monad
,请使用语法最简单地实现scalaz
:
import scala.language.higherKinds
trait Monad[M[_]] {
def point[A](a: => A): M[A]
def bind[A, B](fa: M[A])(f: A => M[B]): M[B]
}
implicit class MonadPointer[A](a: A) {
def point[M[_]: Monad] = implicitly[Monad[M]].point(a)
}
implicit class MonadWrapper[M[_]: Monad, A](t: M[A]) {
private def m = implicitly[Monad[M]]
def flatMap[B](f: A => M[B]): M[B] = m.bind(t)(f)
def >>=[B](f: A => M[B]): M[B] = flatMap(f)
def map[B](f: A => B): M[B] = m.bind(t)(a => m.point(f(a)))
def flatten[B](implicit f: A => M[B]) = m.bind(t)(f)
}
答案 1 :(得分:9)
要成为monad,scala类不需要扩展特定类或混合特定特征。它只需要
SomeClass[T]
)unit
”方法(实际上可能使用任何方法名称,但通常命名为匹配monad的className - c.f. List(x)
和Try(doSomething())
)实施flatMap
方法(a.k.a.“bind
”):
Object SomeClass[T] {
def SomeClass(t: T): SomeClass[T] = ...
}
class SomeClass[T] {
def flatMap[U](T => SomeClass[U]): SomeClass[U] = ...
}
这是通过结构类型/鸭子类型定义而不是通过类型扩展定义。
此外,为了在技术上有资格作为Monad,实现必须满足三个monad定律(其中m为类型SomeClass[T]
而单位= SomeClass[T](t)
对于某些t: T
)。
Monad身份法:绑定monad与单位保持不变
m flatMap unit = m flatMap SomeClass(_) = m
单子单位法:具有任意功能的绑定单位,与将该函数应用于单位的值相同
unit flatMap f = SomeClass(t) flatMap f = f(t) (where f: T => Any)
Monad组成法:bind是关联的
(m flatMap f(_)) flatMap g(_) = m flatMap (t => f(t) flatMap(u => g(u))
(where f: T => SomeClass[U] and g: U => SomeClass[V] for some U and V)
参考:http://james-iry.blogspot.com.au/2007/10/monads-are-elephants-part-3.html
编辑:
如果您正在寻找实现的快捷方式,您可以定义一个共同的祖先,它提供flatMap的标准定义:
trait Monad[T] {
def map[U](f: T => U): Monad[U]
def flatten: Monad[T]
def flatMap[V](g: T => Monad[V]): Monad[V] = map(g) flatten
}
但是你必须为map
&amp;定义具体的实现。 flatten
。这些是设计的结果 - 实际上有无限的可能性来满足这些特征(即不能在以太网中自动找到并且没有通过物理定律来定义;)
答案 2 :(得分:1)
请注意,即使深入了解Scala或Haskell中的代码实现细节,我还要注意,有一件事是有一个类,你知道一种方法来添加单位和乘法,另一种是在有一般情况时。< / p>
在一般情况下,我知道的唯一解决方案是投入免费的monad F | - &gt; 1 + F(1 + F(1 + F(...)))。这根本不存在。
否则,你必须证明你所引入的任何单位乘法都符合monad定律(参见GlenBest的回应。