Как сделать свою собственную для понимания совместимую монаду scala?

Я хочу реализовать свои собственные совместимые с пониманием монады и функторы в Scala.

давайте возьмем две глупые монады в качестве примера. Одна монада-это монада состояния, содержащая "Int", которую вы можете отобразить или отобразить.

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

другая монада принимает делает функциональную композицию так...

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

пример может иметь некоторые ошибки, но вы получите идею.

Я хочу иметь возможность использовать эти два разных типа составленных монад внутри для понимания в Scala. Вот так:

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

Я не мастер Scala, но вы понимаете идею

1 ответов


для типа, который будет использоваться в рамках понимания, вам действительно нужно только определить map и flatMap способы для этого, которые возвращают экземпляры того же типа. Синтаксически for-comprehension преобразуется компилятором в ряд flatMapс последующим окончательным map на 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
}

у меня есть общая черта с двумя подтипами (я мог бы иметь столько, сколько хотел). Общая черта 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)

кроме того, вы можете добавить foreach и filter. Если вы хотите также справиться с этим (без выхода):

for(a <- maybe) println(a)

добавить foreach. И если вы хотите использовать if охранники:

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

вам понадобится filter или withFilter.

полный пример:

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