gpt4 book ai didi

scala - Monad for-comprehensions with implicit Monad 失败,使用继承?

转载 作者:行者123 更新时间:2023-12-02 00:52:16 27 4
gpt4 key购买 nike

我在 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] 抽象类是扩展什么,如果你想成为一个理解,任何结合 FilterMonadic 与 scalaz 的例子?

  1. 我可以重用我的 Monad 隐式而不必重新定义 map、flatMap 等吗?

  2. 我能否保留我的类型别名而不必绕回 Either 或者更糟的是,为 ErrorM 重新定义案例类?

使用 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

由于大致相同的原因无法编译:尝试绑定(bind)两个不同的 monad,List 和 Either。

scala 中的 for comprehension will 或 haskell 中的 do 表达式只会对相同的底层 monad 起作用,因为它们基本上都是对 flatMap>> 系列的句法翻译= 分别。那些仍然需要进行类型检查。

如果你想编写 monad,你可以使用 monad 转换器 (EitherT),尽管在你上面的例子中我认为你不想这样做,因为你实际上想在最后排序。

最后,在我看来,最优雅的代码表达方式是:

def convert(args: List[String]) = args.traverse(toIntSafe)

因为 map 后接 sequencetraverse

关于scala - Monad for-comprehensions with implicit Monad 失败,使用继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56688609/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com