If
do
x <- y
f x
相当于:
y >>= x -> f x
然后
为什么会这样("检索文件最后一行的逗号分隔字段数"):
λ> lline <- (last . lines) <$> readFile fname
λ> length $ split "," lline
11
如果我做对了,我/认为/在一个类似做的前奏会话中,我可以像这样翻译:
x := lline
y := (last . lines) <$> readFile fname
f := (lline -> length $ split "," lline)
不等于
λ> ((last . lines) <$> readFile fname) >>= (lline -> (length $ split "," lline))
<interactive>:97:53-76: error:
• Couldn't match expected type ‘IO b’ with actual type ‘Int’
• In the expression: (length $ split "," lline)
或者,我尝试"无点"翻译
λ> ((last . lines) <$> readFile fname) >>= (length $ split ",")
<interactive>:154:42-59: error:
• Couldn't match expected type ‘String -> IO b’
with actual type ‘Int’
?
我想将该 Int 分配给一个值。 也许我在错误的地方有括号或以其他方式翻译错误?
一方面,我看到这"有效":
λ> ((last . lines) <$> readFile fname) >>= (x -> putStrLn $ show $ length $ split "," x)
11
所以我知道我离得很近。 这对于打印来说很好,但是如何将 Int 分配给这种翻译和脱糖形式的某个地方?
即使是我直觉的、可怕的菜鸟想法也试图用 read:
λ> val = read $ ((last . lines) <$> readFile fname) >>= (x -> length $ split "," x) :: Int
自然是失败的。
正如你所说,以下两个是等效的:
do x <- y
f x
y >>= (x -> f x)
但是,为了实际工作,我们必须尊重(>>=)
的类型:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
如果我们看看你的表情,我们会发现有些地方不太对劲:
((last . lines) <$> readFile fname) :: IO String -- seems right
(lline -> (length $ split "," lline)) :: String -> Int -- no IO on the right hand side!
你的函数length . split ","
不返回IO
的东西,它返回一个Int
。因此,您要么必须使用print
或类似的东西来显示它,要么返回一个IO Int
(取决于您要做什么):
((last . lines) <$> readLine fname) >>= print . length . split ","
请记住,GHCi 并不是一个真正的"正常"do
块,否则简单的表达式,如
ghci> 1 + 1
2
会导致类型错误。相反,GHCi 会检查类型是否为IO a
。如果是,则运行操作,如果a
是Show
的实例,则它还将显示结果:
ghci> data Foo = Foo -- no instance of Show
ghci> return Foo -- no output!
ghci> return (1 + 1)
2
如果你有另一个表达式x
没有类型IO a
,它将充当print x
,包括Show
实例错误:
ghci> Foo
No instance for (Show Foo) arising from a use of `print'
ghci> 1 + 1
2
TL;博士
注意类型,并记住GHCi有很多方便的魔力。