Dan*_*ton 40 monads scala typeclass either for-comprehension
据我了解,Scala"for"语法与Haskell的monadic"do"语法非常相似.在Scala中,"for"语法通常用于Lists和Options.我想将它与Eithers 一起使用,但默认导入中不存在必要的方法.
for {
foo <- Right(1)
bar <- Left("nope")
} yield (foo + bar)
// expected result: Left("nope")
// instead I get "error: value flatMap is not a member..."
Run Code Online (Sandbox Code Playgroud)
这个功能是通过一些导入提供的吗?
有一个轻微的障碍:
for {
foo <- Right(1)
if foo > 3
} yield foo
// expected result: Left(???)
Run Code Online (Sandbox Code Playgroud)
对于列表,它将是List().因为Option,它会None.Scala标准库是否为此提供了解决方案?(或许scalaz?)怎么样?假设我想为Either提供我自己的"monad实例",我怎么能这样做?
Dan*_*ral 53
它在scala 2.11及更早版本中不起作用,因为Either它不是monad.虽然有关于右偏倚的说法,但你不能在理解中使用它:你必须得到一个LeftProject或者RightProjection,如下所示:
for {
foo <- Right[String,Int](1).right
bar <- Left[String,Int]("nope").right
} yield (foo + bar)
Run Code Online (Sandbox Code Playgroud)
Left("nope")顺便说一下,这又回来了.
在Scalaz上,你将替换Either为Validation.有趣的事实:Either原作者是Scalaz的一位作者Tony Morris.他想做出Either正确的偏见,但同事却说服了.
mis*_*tor 16
这个功能是通过一些导入提供的吗?
是的,但通过第三方导入:Scalaz提供了一个Monad实例Either.
import scalaz._, Scalaz._
scala> for {
| foo <- 1.right[String]
| bar <- "nope".left[Int]
| } yield (foo.toString + bar)
res39: Either[String,java.lang.String] = Left(nope)
Run Code Online (Sandbox Code Playgroud)
现在if-guard不是一个monadic操作.因此,如果您尝试使用if-guard,则会导致编译器错误.
scala> for {
| foo <- 1.right[String]
| if foo > 3
| } yield foo
<console>:18: error: value withFilter is not a member of Either[String,Int]
foo <- 1.right[String]
^
Run Code Online (Sandbox Code Playgroud)
-上面所用的方便的方法.right和.left-也可从Scalaz.
编辑:
我错过了你的这个问题.
假设我想为Either提供我自己的"monad实例",我怎么能这样做?
Scala的for内涵是简单地翻译为.map,.flatMap,.withFilter,并参与对对象的调用.(您可以在此处找到完整的转换方案.)因此,如果某个类没有所需的方法,您可以使用隐式转换将它们添加到类中..filter.foreach
下面是一个新的REPL会议.
scala> implicit def eitherW[A, B](e: Either[A, B]) = new {
| def map[B1](f: B => B1) = e.right map f
| def flatMap[B1](f: B => Either[A, B1]) = e.right flatMap f
| }
eitherW: [A, B](e: Either[A,B])java.lang.Object{def map[B1](f: B => B1): Product
with Either[A,B1] with Serializable; def flatMap[B1](f: B => Either[A,B1]):
Either[A,B1]}
scala> for {
| foo <- Right(1): Either[String, Int]
| bar <- Left("nope") : Either[String, Int]
| } yield (foo.toString + bar)
res0: Either[String,java.lang.String] = Left(nope)
Run Code Online (Sandbox Code Playgroud)
Mat*_*ein 12
从文档:
由于要么定义方法map和flatMap,它也可以用于理解:
Run Code Online (Sandbox Code Playgroud)val right1: Right[Double, Int] = Right(1) val right2 = Right(2) val right3 = Right(3) val left23: Left[Double, Int] = Left(23.0) val left42 = Left(42.0) for ( a <- right1; b <- right2; c <- right3 ) yield a + b + c // Right(6) for ( a <- right1; b <- right2; c <- left23 ) yield a + b + c // Left(23.0) for ( a <- right1; b <- left23; c <- right2 ) yield a + b + c // Left(23.0) // It is advisable to provide the type of the “missing” value (especially the right value for `Left`) // as otherwise that type might be infered as `Nothing` without context: for ( a <- left23; b <- right1; c <- left42 // type at this position: Either[Double, Nothing] ) yield a + b + c // ^ // error: ambiguous reference to overloaded definition, // both method + in class Int of type (x: Char)Int // and method + in class Int of type (x: Byte)Int // match argument types (Nothing)