在Scala中创建一个任意类作为monad实例

jde*_*lop 15 monads scala

为了在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)
Run Code Online (Sandbox Code Playgroud)

所以我没有触及Z的定义,而是将其作为monad

我如何在Scala中执行此操作?似乎除了混合一些特征和定义方法map/flatMap/filter/withFilter之外别无他法?

sen*_*nia 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))
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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)
Run Code Online (Sandbox Code Playgroud)

for-comprehensions(类似于do-notation):

for {
  i <- z
  j <- (i + 1).point[Z]
  k = i + j
} yield i * j * k
// Z[Int] = MyZRight(6)
Run Code Online (Sandbox Code Playgroud)

另请参阅Scalaz cheatsheetLearning scalaz.

没有魔力scalaz- 你可以在没有魔法的情况下实施scalaz.

相关:Scala和Haskell中的Typeclases.

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)
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么选择downvote?我该如何改进这个答案?(注意OP应该已经知道`Monad`是什么了,因为他在`haskell`中实现了它). (4认同)

Gle*_*est 10

要成为monad,scala类不需要扩展特定类或混合特定特征.它只需要

  • 是一个类型参数化的类(SomeClass[T])
  • 实现" unit"方法(可能实际上使用任何方法名称,但通常命名为匹配monad的className - cf List(x)Try(doSomething()))
  • 实现flatMap方法(又名" bind"):

    Object SomeClass[T] {
        def SomeClass(t: T): SomeClass[T] = ...
    }
    class SomeClass[T] {
        def flatMap[U](T => SomeClass[U]): SomeClass[U] = ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

这是通过结构类型/鸭子类型定义而不是通过类型扩展定义.

此外,为了在技术上有资格作为Monad,实现必须满足三个monad定律(其中m是类型SomeClass[T],单位= SomeClass[T](t)对于某些t: T).

  1. Monad身份法:绑定monad与单位保持不变

      m flatMap unit = m flatMap SomeClass(_) = m
    
    Run Code Online (Sandbox Code Playgroud)
  2. Monad单位法:具有任意功能的绑定单位,与将该函数应用于单位的值相同

      unit flatMap f = SomeClass(t) flatMap f = f(t)           (where f: T => Any)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 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)
    
    Run Code Online (Sandbox Code Playgroud)

参考: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
}
Run Code Online (Sandbox Code Playgroud)

但是你必须为map&定义具体的实现flatten.这些是设计的结果 - 实际上有无限的可能性来满足这些特征(即不能在以太中自动找到并且不是通过物理定律来定义的;))