Как сделать свою собственную для понимания совместимую монаду 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 = ()
}