如何在Scala中编写Haskell-do-notation

时间:2017-07-27 06:56:46

标签: scala haskell

我使用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")
    }

你能告诉我你以漂亮的方式写作的想法吗?

2 个答案:

答案 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中你可以直接returnyield是一个关键字,而不是像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