我使用do-nation编写了以下Haskell代码。
我想将其转换为Scala代码
main :: IO ()
main = do
print $ func1 (Right 1) (Right 2)
print $ func1 (Right 10) (Right 3)
func1 :: Either String Int -> Either String Int -> Either String Double
func1 e1 e2 = do
v1 <- e1
v2 <- e2
if v1 < v2
then Right 1.515151 -- No meaning
else Left "some error"
这是Haskell的输出
Right 1.515151
Left "some error"
我写了如下Scala代码。但是当我查看result <- if(v1 < v2)...
和yield result
时,我感到很奇怪。
object Main {
def main(args: Array[String]): Unit = {
println(func1(Right(1))(Right(2)))
println(func1(Right(10))(Right(3)))
}
def func1(e1: Either[String, Int])(e2: Either[String, Int]): Either[String, Double] =
for{
v1 <- e1
v2 <- e2
// Weird...
result <- if(v1 < v2)
Right(1.515151)
else
Left("some error")
} yield result
}
这是Scala的输出
Right(1.515151)
Left(some error)
我想写下来。但斯卡拉不允许我写作。
// Invalid Scala Code
def func1(e1: Either[String, Int])(e2: Either[String, Int]): Either[String, Double] =
for{
v1 <- e1
v2 <- e2
} {
if(v1 < v2)
Right(1.515151)
else
Left("some error")
}
你能告诉我你以漂亮的方式写作的想法吗?
答案 0 :(得分:5)
它可以被美化一些。
for {
v1 <- e1
v2 <- e2
res <- Either.cond(v1 < v2, 1.515151, "some error")
} yield res
仅仅提出一个保护条件会很好,但根据Scala docs,这是不合适的,因为Either
没有withFilter
方法。
答案 1 :(得分:2)
(免责声明:我不知道Haskell,所以我可能错了)
Haskell的do
符号与Scala的for
/ yield
理解之间的区别在于do
序列以bind
结尾(即{{1} }),但flatMap
/ for
以正常yield
结尾。
所以在Haskell中如果你在最后一步中有一个纯值,你必须将它包装在map
中,但在Scala中你可以直接return
。 yield
是一个关键字,而不是像Haskell yield
这样的函数。另一方面,当在最后一步中你有一个monadic值,在Haskell中你可以把它放在那里,但在Scala中你需要添加return
然后result <- monadicValue
的步骤。
这只是这两种语言的设计差异,我相信你只需要习惯Scala是如何做到的。
关于你对其他答案的评论中的问题:
在scalaz而不是yield result
中,您可以使用Either.cond(p, a, b)
,它会返回一个析取:
p.either(a).or(b)
然后你可以将这个析取包装在你想要的monad中,然后放在scala> import scalaz._, Scalaz._
scala> true.either(10).or("error")
res0: scalaz.\/[String,Int] = \/-(10)
中。例如:
EitherT