如何制作自己的符合理解标准的 scala monad?
How to make your own for-comprehension compliant scala monad?
我想在 Scala 中实现我自己的 for-comprehension 兼容单子和仿函数。
我们以两个愚蠢的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 高手,但你懂的
对于要在 for-comprehension 中使用的类型,您实际上只需要为相同类型的 return 个实例定义 map
和 flatMap
方法。从句法上讲,编译器将 for-comprehension 转换为一系列 flatMap
,然后是 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)
此外,您可以添加 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 = ()
}
我想在 Scala 中实现我自己的 for-comprehension 兼容单子和仿函数。
我们以两个愚蠢的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 高手,但你懂的
对于要在 for-comprehension 中使用的类型,您实际上只需要为相同类型的 return 个实例定义 map
和 flatMap
方法。从句法上讲,编译器将 for-comprehension 转换为一系列 flatMap
,然后是 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)
此外,您可以添加 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 = ()
}