我在斯卡拉https://github.com/scala/bug/issues/2823碰上这张著名的10岁门票
因为我期望理解能像Haskell中的do-blocks一样工作。而且为什么不呢,Monads配上一层糖就很棒了。在这一点上,我有这样的事情:
import scalaz.{Monad, Traverse}
import scalaz.std.either._
import scalaz.std.list._
type ErrorM[A] = Either[String, A]
def toIntSafe(s : String) : ErrorM[Int] = {
try {
Right(s.toInt)
} catch {
case e: Exception => Left(e.getMessage)
}
}
def convert(args: List[String])(implicit m: Monad[ErrorM], tr: Traverse[List]): ErrorM[List[Int]] = {
val expanded = for {
arg <- args
result <- toIntSafe(arg)
} yield result
tr.sequence(expanded)(m)
}
println(convert(List("1", "2", "3")))
println(convert(List("1", "foo")))
我得到了错误
“值映射表不是ErrorM [Int]的成员” 结果<-toIntSafe(arg)
如何恢复我惯常的美丽单调的理解?一些研究表明,FilterMonadic[A, Repr]
抽象类是一个扩展类,如果您想成为一个理解者,可以将FilterMonadic
与scalaz结合使用的任何示例?
我可以隐式重用Monad,而不必重新定义map,flatMap等吗?
我是否可以保留我的类型别名,而不必重新包装ErrorM的大小写类?
使用Scala 2.11.8
编辑:我添加了Haskell代码,以表明它确实在GHC中确实有效,而没有显式的Monad转换器,仅遍历和Either和List的默认monad实例。
type ErrorM = Either String
toIntSafe :: Read a => String -> ErrorM a
toIntSafe s = case reads s of
[(val, "")] -> Right val
_ -> Left $ "Cannot convert to int " ++ s
convert :: [String] -> ErrorM [Int]
convert = sequence . conv
where conv s = do
arg <- s
return . toIntSafe $ arg
main :: IO ()
main = do
putStrLn . show . convert $ ["1", "2", "3"]
putStrLn . show . convert $ ["1", "foo"]
答案 0 :(得分:2)
您的haskell代码和scala代码不相同:
do
arg <- s
return . toIntSafe $ arg
对应于
for {
arg <- args
} yield toIntSafe(arg)
编译得很好。
要了解为什么您的示例无法编译,我们可以对其进行解糖:
for {
arg <- args
result <- toIntSafe(arg)
} yield result
=
args.flatMap { arg =>
toIntSafe(arg).map {result => result}
}
现在查看类型:
args: List[String]
args.flatMap: (String => List[B]) => List[B]
arg => toIntSafe(arg).map {result => result} : String => ErrorM[Int]
哪个显示了问题。 flatMap
期望一个函数返回一个List
,但是您给它一个函数返回一个ErrorM
。
Haskell代码如下:
do
arg <- s
result <- toIntSafe arg
return result
由于大致相同的原因,它们都不会编译:试图绑定两个不同的Monad,即List和Either。
在scala中理解A或在haskell中执行do表达式仅适用于相同的基础monad,因为它们基本上都是分别对flatMap
和>>=
系列的句法翻译。那些仍然需要进行类型检查。
如果您想组成单子,则可以使用单子转换器(EitherT),尽管在上面的示例中,我认为您不想这样做,因为您实际上想在最后进行排序。
最后,我认为表达代码的最优雅的方式是:
def convert(args: List[String]) = args.traverse(toIntSafe)
因为map
后跟sequence
是traverse