如何使自己的符合理解能力的scala monad?

Mic*_*tte 10 monads scala

我想在Scala中实现我自己的for-comprehension兼容的monad和functor.

我们以两个愚蠢的monad为例.一个monad是一个状态monad,包含一个可以映射或平面映射的"Int".

val maybe = IntMonad(5)
maybe flatMap( a => 3 * ( a map ( () => 2 * a ) ) )
// returns IntMonad(30)
Run Code Online (Sandbox Code Playgroud)

另一个monad采取像这样的功能组成......

val func = FunctionMonad( () => println("foo") )
val fooBar = func map ( () => println("bar") )
fooBar()
// foo
// bar
// returns Unit
Run Code Online (Sandbox Code Playgroud)

这个例子可能有一些错误,但你明白了.

我希望能够在Scala中使用这两种不同类型的Monad组成的for-understanding.像这样:

val myMonad = IntMonad(5)
for {
    a <- myMonad
    b <- a*2
    c <- IntMonad(b*2)
} yield c    
// returns IntMonad(20)
Run Code Online (Sandbox Code Playgroud)

我不是斯卡拉大师,但你明白了

Mic*_*jac 17

对于中要使用的类型换理解,你真的只需要定义map,并flatMap为它返回同一类型的实例方法.从语法上讲,for-comprehension被编译器转换为一系列flatMaps,然后是mapfor的final yield.只要这些方法具有适当的签名,它就可以工作.

我不确定你的例子是什么,但这是一个简单的例子,相当于Option:

sealed trait MaybeInt {
    def map(f: Int => Int): MaybeInt
    def flatMap(f: Int => MaybeInt): MaybeInt
}

case class SomeInt(i: Int) extends MaybeInt {
    def map(f: Int => Int): MaybeInt = SomeInt(f(i))
    def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
}

case object NoInt extends MaybeInt {
    def map(f: Int => Int): MaybeInt = NoInt
    def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
}
Run Code Online (Sandbox Code Playgroud)

我有两个子类型的共同特征(尽管我可以拥有尽可能多的子类型).公共特征MaybeInt强制每个子类型符合map/ flatMap接口.

scala> val maybe = SomeInt(1)
maybe: SomeInt = SomeInt(1)

scala> val no = NoInt
no: NoInt.type = NoInt

for {
  a <- maybe
  b <- no
} yield a + b

res10: MaybeInt = NoInt

for {
  a <- maybe
  b <- maybe
} yield a + b

res12: MaybeInt = SomeInt(2)
Run Code Online (Sandbox Code Playgroud)

此外,您可以添加foreachfilter.如果你想处理这个(没有收益):

for(a <- maybe) println(a)
Run Code Online (Sandbox Code Playgroud)

你会添加foreach.如果你想使用if警卫:

for(a <- maybe if a > 2) yield a
Run Code Online (Sandbox Code Playgroud)

你需要filterwithFilter.

一个完整的例子:

sealed trait MaybeInt { self =>
    def map(f: Int => Int): MaybeInt
    def flatMap(f: Int => MaybeInt): MaybeInt
    def filter(f: Int => Boolean): MaybeInt
    def foreach[U](f: Int => U): Unit
    def withFilter(p: Int => Boolean): WithFilter = new WithFilter(p)

    // Based on Option#withFilter
    class WithFilter(p: Int => Boolean) {
        def map(f: Int => Int): MaybeInt = self filter p map f
        def flatMap(f: Int => MaybeInt): MaybeInt = self filter p flatMap f
        def foreach[U](f: Int => U): Unit = self filter p foreach f
        def withFilter(q: Int => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
    }
}

case class SomeInt(i: Int) extends MaybeInt {
    def map(f: Int => Int): MaybeInt = SomeInt(f(i))
    def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
    def filter(f: Int => Boolean): MaybeInt = if(f(i)) this else NoInt
    def foreach[U](f: Int => U): Unit = f(i)
}

case object NoInt extends MaybeInt {
    def map(f: Int => Int): MaybeInt = NoInt
    def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
    def filter(f: Int => Boolean): MaybeInt = NoInt
    def foreach[U](f: Int => U): Unit = ()
}
Run Code Online (Sandbox Code Playgroud)