如果
do
x <- y
f x
相当于:
y >>= \x -> f x
然后
为什么会这样(“检索文件最后一行的逗号分隔字段数”):
λ> lline <- (last . lines) <$> readFile fname
λ> length $ split "," lline
11
如果我这样做的话,我/想/在类似Prelude的会话中,我可以这样翻译:
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
自然会失败。
答案 0 :(得分:3)
正如你所说,以下两个是等价的:
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并非真正的&#34;正常&#34; 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
注意类型并记住GHCi为方便起见有很多魔力。