[英]How does Haskell "desugar" getline in this do block?
我读过几本关于 Haskell 的书,但没有编写太多代码,而且我对 Haskell 在某种情况下的作用有点困惑。 假设我正在使用getLine
,这样用户可以按一个键继续,但我真的不想以任何有意义的方式解释那个人的输入。 我相信这是这样做的有效方法:
main = do
_ <- getLine
putStrLn "foo"
我了解这是在做什么的基本要点。 getLine
返回一个IO String
,而putStrLn
接受一个String
并返回IO ()
,所以如果我理论上想打印用户在控制台中输入的内容,我基本上会使用来自Monad
class 的>>=
运算符。在我的例子中,我相信我的代码等同于getLine >> putStrLn "foo"
因为我丢弃了getLine
的返回值。
但是,如果我这样做呢?
main = do
let _ = getLine
putStrLn "foo"
在这种情况下,我们正在设置一种 lambda 来处理需要IO String
的东西,对吗? 我可以写一个printIOString
function 来打印用户的输入,这样就可以了。 但是,当我实际上没有使用IO String
时,程序的行为很奇怪...... getLine
甚至不提示我输入; 该程序只是打印出"foo"
。
我不太确定这里的“脱糖”语法是什么,或者这是否会揭示 Haskell 在幕后所做的事情。
让我们用几个更复杂的例子来热身。
main = do
x
x
x
putStrLn "foo"
where
x = do
getLine
你希望它做什么? 我不了解你,但我期望程序获得三行然后打印一些东西。 如果我们对第二个do
块进行脱糖处理,我们会得到
main = do
x
x
x
putStrLn "foo"
where x = getLine
因为这是另一个的脱糖,所以它的行为相同,在打印前得到三行。 如果您觉得第一个不直观,还有另一种思路可以得出相同的答案。 “引用透明”是Haskell的定义特性之一,准确的意思是你可以用它的定义替换对某个东西(即变量名)的“引用”,所以前面的程序应该和下面的程序完全一样
main = do
getLine
getLine
getLine
putStrLn "foo"
如果我们认真对待方程x = getLine
。 好的,所以我们有一个读取三行并打印的程序。 这个如何?
main = do
x
x
putStrLn "foo"
where x = getLine
获取两行并打印。 还有这个?
main = do
x
putStrLn "foo"
where x = getLine
得到一行然后打印。 希望你看到这是怎么回事......
main = do
putStrLn "foo"
where x = getLine
获取零行然后打印,即立即打印! 我使用where
而不是let
来使开头的示例更加明显,但是您几乎总是可以用它的let
表亲替换where
块而不改变其含义:
main = let x = getLine in do
putStrLn "foo"
由于我们不引用x
,我们甚至不需要命名它:
main = let _ = getLine in do
putStrLn "foo"
这是您编写的代码的脱糖。
第一种情况像您预期的那样脱糖:
main = getLine >>= \_ -> putStrLn "foo"
这相当于
main = getLine >> putStrLn "foo"
在第二种情况下,
main = do
let _ = getLine
putStrLn "foo"
被脱糖为
main = let _ = getLine in putStrLn "foo"
由于不需要_ = getLine
值来评估let
表达式的 RHS,因此编译器可以随意忽略它并且永远不会执行 IO 效果,这就是为什么不再提示您进行 CLI 输入的原因。
尽管这两种情况都忽略了getLine
的结果,但区别在于第一种情况在IO
上下文中评估getLine
,而第二种情况将getLine
评估为纯值。 在IO
中,副作用必须一起执行和排序,但在纯上下文中,编译器可以自由地忽略未使用的值。
我不建议这样做,因为它不是很惯用,但你可以写类似的东西
printIOString :: IO String -> IO ()
printIOString ios = ios >>= putStrLn
并像printIOString getLine
一样使用它
根据https://stackoverflow.com/tags/do-notation/info ,
do { let { _ = getLine } ; putStrLn "foo" }
=
do { let { _ = getLine } in putStrLn "foo" }
=
let { _ = getLine } in putStrLn "foo"
Haskell 语义等同于
getLine & (\ _ -> putStrLn "foo")
=
putStrLn "foo"
(与x & f = fx
),而确实
do { _ <- getLine ; putStrLn "foo" }
=
getLine >>= (\ _ -> putStrLn "foo")
无法进一步简化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.