【问题标题】:How to make your own for-comprehension compliant scala monad?如何制作自己的理解兼容的scala monad?
【发布时间】:2016-06-16 02:55:27
【问题描述】:

我想在 Scala 中实现我自己的易于理解的 monad 和 functor。

让我们以两个愚蠢的 monad 为例。一个 monad 是一个状态 monad,它包含一个“Int”,您可以对其进行映射或平面映射。

val maybe = IntMonad(5)
maybe flatMap( a => 3 * ( a map ( () => 2 * a ) ) )
// returns IntMonad(30)

另一个 monad 采用这样的函数组合...

val func = FunctionMonad( () => println("foo") )
val fooBar = func map ( () => println("bar") )
fooBar()
// foo
// bar
// returns Unit

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

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

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

我不是 Scala 大师,但你懂的

【问题讨论】:

    标签: scala monads


    【解决方案1】:

    对于要在 for-comprehension 中使用的类型,您实际上只需要为其定义返回相同类型实例的 mapflatMap 方法。在语法上,编译器将 for-comprehension 转换为一系列 flatMaps,然后是 yield 的最终 map。只要这些方法具有适当的签名,它就可以工作。

    我不太确定你的例子是什么,但这是一个相当于 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
    }
    

    我有两个子类型的共同特征(不过,我可以拥有尽可能多的子类型)。公共特征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)
    

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

    for(a <- maybe) println(a)
    

    您可以添加foreach。如果你想使用if 守卫:

    for(a <- maybe if a > 2) yield a
    

    您需要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 = ()
    }
    

    【讨论】:

    • 使用 Scalaz Monad 类型而不是定义自己的类型会更好吗?
    • filter api 也需要“解包” monad 项目。例如,如果您使用与 MaybeInt 类似的 MaybeAB 但对于 case class AB(a: Int, b: Int),则需要过滤器来执行 for( AB(a,b) &lt;- maybAB) ...
    猜你喜欢
    • 2018-03-10
    • 2018-05-05
    • 2012-09-15
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-15
    相关资源
    最近更新 更多