如何制作自己的全面兼容Scala Monad? [英] How to make your own for-comprehension compliant scala monad?

查看:62
本文介绍了如何制作自己的全面兼容Scala Monad?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Scala中实现自己与理解兼容的monad和函子.

I want to implement my own for-comprehension compatible monads and functors in Scala.

让我们以两个愚蠢的monad为例.一个monad是包含一个"Int"的州monad,您可以在其上进行映射或平面映射.

Let's take two stupid monads as an example. One monad is a state monad that contains an "Int" that you can map or flatmap over.

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

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

Another monad takes does function composition like so...

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

该示例可能有一些错误,但是您明白了.

The example may have some mistakes, but you get the idea.

我希望能够在Scala的理解中使用这两种不同类型的Monads.像这样:

I want to be able to use these two different types of made up Monads inside a for-comprehension in Scala. Like this:

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

我不是Scala大师,但您明白了

I am not a Scala master, but you get the idea

推荐答案

对于要在理解中使用的类型,您实际上只需要为其定义mapflatMap方法即可返回该实例的实例.相同的类型.从句法上讲,理解力由编译器转换为一系列flatMap,随后是yield的最终map.只要这些方法带有适当的签名,就可以使用.

For a type to be used within a for-comprehension, you really only need to define map and flatMap methods for it that return instances of the same type. Syntactically, the for-comprehension is transformed by the compiler into a series of flatMaps followed by a final map for the yield. As long as these methods are available with the appropriate signature, it will work.

我不太确定您对示例的追求是什么,但这是一个与Option等效的简单示例:

I'm not really sure what you're after with your examples, but here is a trivial example that is equivalent to 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接口.

I have a common trait with two sub-types (I could have as many as I wanted, though). The common trait MaybeInt enforces each sub-type to conform to the map/flatMap interface.

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.如果您还想处理此问题(无收益):

Additionally, you can add foreach and filter. If you want to also handle this (no yield):

for(a <- maybe) println(a)

您将添加foreach.如果要使用if防护罩:

You would add foreach. And if you want to use if guards:

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

这篇关于如何制作自己的全面兼容Scala Monad?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆