Monad for-comprehensions with implicit Monad 失败,使用继承?
Monad for-comprehensions with implicit Monad fails, use inheritance?
我 运行 喜欢这张著名的 Scala 10 年前的票 https://github.com/scala/bug/issues/2823
因为我希望 for-comprehensions 像 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")))
我收到错误
"Value map is not a member of ErrorM[Int]"
result <- toIntSafe(arg)
我如何回到我习惯的美丽的一元理解?一些研究表明 FilterMonadic[A, Repr]
摘要 class 是什么,如果你想成为一个理解,任何结合 FilterMonadic
和 scalaz 的例子?
我可以重复使用我的 Monad 隐式而不必重新定义 map、flatMap 等吗?
我可以保留我的类型别名而不必回绕吗?或者更糟,为 ErrorM 重新定义大小写 classes?
使用 Scala 2.11.8
编辑:我正在添加 Haskell 代码来证明这在没有显式 Monad 转换器的 GHC 中确实有效,只有 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"]
你的 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 中的 for comprehension will 或 haskell 中的 do 表达式只会对相同的底层 monad 起作用,因为它们基本上都是对一系列 flatMap
和 [=分别为 20=]s。而那些仍然需要进行类型检查。
如果你想编写 monad,你可以使用 monad 转换器 (EitherT),尽管在你上面的例子中我认为你不想这样做,因为你实际上想在最后排序。
最后,在我看来,最优雅的代码表达方式是:
def convert(args: List[String]) = args.traverse(toIntSafe)
因为map
后面跟着sequence
是traverse
我 运行 喜欢这张著名的 Scala 10 年前的票 https://github.com/scala/bug/issues/2823
因为我希望 for-comprehensions 像 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")))
我收到错误
"Value map is not a member of ErrorM[Int]" result <- toIntSafe(arg)
我如何回到我习惯的美丽的一元理解?一些研究表明 FilterMonadic[A, Repr]
摘要 class 是什么,如果你想成为一个理解,任何结合 FilterMonadic
和 scalaz 的例子?
我可以重复使用我的 Monad 隐式而不必重新定义 map、flatMap 等吗?
我可以保留我的类型别名而不必回绕吗?或者更糟,为 ErrorM 重新定义大小写 classes?
使用 Scala 2.11.8
编辑:我正在添加 Haskell 代码来证明这在没有显式 Monad 转换器的 GHC 中确实有效,只有 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"]
你的 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 中的 for comprehension will 或 haskell 中的 do 表达式只会对相同的底层 monad 起作用,因为它们基本上都是对一系列 flatMap
和 [=分别为 20=]s。而那些仍然需要进行类型检查。
如果你想编写 monad,你可以使用 monad 转换器 (EitherT),尽管在你上面的例子中我认为你不想这样做,因为你实际上想在最后排序。
最后,在我看来,最优雅的代码表达方式是:
def convert(args: List[String]) = args.traverse(toIntSafe)
因为map
后面跟着sequence
是traverse