2017-07-27 80 views
4

我使用do-nation编写了以下Haskell代码。如何在Scala中编写Haskell-do-notation

,我想将它转化成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" 

这里是哈斯克尔

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 
} 

这里是斯卡拉

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") 
    } 

你能告诉我你用美丽的方式写作的想法吗?

回答

4

它可以被一些美化。

for { 
    v1 <- e1 
    v2 <- e2 
    res <- Either.cond(v1 < v2, 1.515151, "some error") 
} yield res 

这将是很好,只是扔在监护条件,但根据Scala docs,这不是支持,因为Either没有withFilter方法。

+0

非常感谢你,jwvh!我不知道'two.cond'。其实我想在scalaz中使用'EitherT'而不是'Either',所以我搜索'EitherT'中的'Either.cond'选项,但是我找不到它。你能告诉我,如果你知道吗? – redstone

+0

对不起,不知道斯卡拉。它在我的TODO清单上。 – jwvh

2

(声明:我不知道哈斯克尔,所以我可能是错与此)

Haskell的do符号之间的差异,以及Scala的for/yield理解的是,do序列与bind(即flatMap结束),但for/yield以正常map结束。

所以在Haskell中,如果你在最后一步有一个纯粹的值,你必须将它包装在return中,但是在Scala中你可以直接使用yieldyield是一个关键字,而不是像Haskell的return这样的函数。另一方面,在最后一步中,您有一个单值,在Haskell中,您可以将它放在那里,但在Scala中,您必须添加一个步骤result <- monadicValue,然后yield result

这只是这两种语言在设计上的差异,我相信你必须习惯Scala如何做到这一点。


至于在其他答案的评论你的问题:

在scalaz代替Either.cond(p, a, b)可以使用p.either(a).or(b),它返回一个脱节:

scala> import scalaz._, Scalaz._ 

scala> true.either(10).or("error") 
res0: scalaz.\/[String,Int] = \/-(10) 

然后你可以用这在你想要的monad中进行析取,然后在EitherT。例如:

scala> EitherT(true.either(10).or("error").some) 
res1: scalaz.EitherT[Option,String,Int] = EitherT(Some(\/-(10))) 
+0

非常感谢Kolmar〜'p。(a)或者(b)'是我想要的。谢谢! >>我相信你只需要习惯Scala如何做到这一点。 我想要做的。 – redstone

+0

是否有'火柴盒'版本?我的代码的最后一个表达式是'if',但scalaz中是否有'match-case'版本? – redstone

+0

'match-case' version意思是'v1 Right(1.515151); case false => Left(“some error”)}'。 (这个例子可以用'if'替换,但这只是一个例子。) – redstone

相关问题