Haskell "desugar" 如何在这个 do 块中获取行?
How does Haskell "desugar" getline in this do block?
我读过几本关于 Haskell 的书,但并没有编写太多代码,而且我对 Haskell 在某些情况下所做的事情感到有点困惑.假设我正在使用 getLine
这样用户可以按一个键继续,但我真的不想以任何有意义的方式解释那个人的输入。我相信这是一种有效的方法:
main = do
_ <- getLine
putStrLn "foo"
我明白这是做什么的基本要点。 getLine
returns 一个 IO String
,putStrLn
需要一个 String
和 returns IO ()
,所以如果我理论上想打印用户在控制台中输入的内容,我基本上会使用 Monad
class 中的 >>=
运算符。就我而言,我相信我的代码等同于 getLine >> putStrLn "foo"
因为我丢弃了 getLine
.
的 return 值
但是,如果我改为这样做呢?
main = do
let _ = getLine
putStrLn "foo"
在这种情况下,我们正在设置一种 lambda 来处理需要 IO String
的东西,对吧?我可以编写一个 printIOString
函数来打印用户的输入,这样就可以正常工作。但是,当我实际上没有使用 IO String
时,该程序的行为很奇怪...... getLine
甚至没有提示我输入;该程序只是打印出 "foo"
.
我不太确定这里的“脱糖”语法是什么,或者这是否会阐明 Haskell 在幕后所做的事情。
第一个案例如您所料脱糖:
main = getLine >>= \_ -> putStrLn "foo"
相当于
main = getLine >> putStrLn "foo"
第二种情况,
main = do
let _ = getLine
putStrLn "foo"
脱糖为
main = let _ = getLine in putStrLn "foo"
由于计算 let
表达式的 RHS 不需要 _ = getLine
值,编译器可以随意忽略它并且永远不会执行 IO 效果,这就是为什么你不再提示输入 CLI。
尽管两种情况都忽略了 getLine
的结果,但区别在于第一种情况在 IO
上下文中评估 getLine
,而第二种情况将 getLine
评估为一个纯粹的价值。在 IO
中,side-effects 必须一起执行和排序,但在纯上下文中,编译器可以自由地忽略未使用的值。
我不推荐这样做,因为它不是很地道,但你可以写成类似
的东西
printIOString :: IO String -> IO ()
printIOString ios = ios >>= putStrLn
并像 printIOString getLine
一样使用它
让我们用几个更复杂的例子来热身。
main = do
x
x
x
putStrLn "foo"
where
x = do
getLine
您希望它做什么?我不了解你,但是 I 期望程序得到三行然后打印一些东西。如果我们对第二个 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"
这是你写的代码的脱糖。
根据https://whosebug.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 = f x
),而实际上
do { _ <- getLine ; putStrLn "foo" }
=
getLine >>= (\ _ -> putStrLn "foo")
无法进一步简化。
我读过几本关于 Haskell 的书,但并没有编写太多代码,而且我对 Haskell 在某些情况下所做的事情感到有点困惑.假设我正在使用 getLine
这样用户可以按一个键继续,但我真的不想以任何有意义的方式解释那个人的输入。我相信这是一种有效的方法:
main = do
_ <- getLine
putStrLn "foo"
我明白这是做什么的基本要点。 getLine
returns 一个 IO String
,putStrLn
需要一个 String
和 returns IO ()
,所以如果我理论上想打印用户在控制台中输入的内容,我基本上会使用 Monad
class 中的 >>=
运算符。就我而言,我相信我的代码等同于 getLine >> putStrLn "foo"
因为我丢弃了 getLine
.
但是,如果我改为这样做呢?
main = do
let _ = getLine
putStrLn "foo"
在这种情况下,我们正在设置一种 lambda 来处理需要 IO String
的东西,对吧?我可以编写一个 printIOString
函数来打印用户的输入,这样就可以正常工作。但是,当我实际上没有使用 IO String
时,该程序的行为很奇怪...... getLine
甚至没有提示我输入;该程序只是打印出 "foo"
.
我不太确定这里的“脱糖”语法是什么,或者这是否会阐明 Haskell 在幕后所做的事情。
第一个案例如您所料脱糖:
main = getLine >>= \_ -> putStrLn "foo"
相当于
main = getLine >> putStrLn "foo"
第二种情况,
main = do
let _ = getLine
putStrLn "foo"
脱糖为
main = let _ = getLine in putStrLn "foo"
由于计算 let
表达式的 RHS 不需要 _ = getLine
值,编译器可以随意忽略它并且永远不会执行 IO 效果,这就是为什么你不再提示输入 CLI。
尽管两种情况都忽略了 getLine
的结果,但区别在于第一种情况在 IO
上下文中评估 getLine
,而第二种情况将 getLine
评估为一个纯粹的价值。在 IO
中,side-effects 必须一起执行和排序,但在纯上下文中,编译器可以自由地忽略未使用的值。
我不推荐这样做,因为它不是很地道,但你可以写成类似
的东西printIOString :: IO String -> IO ()
printIOString ios = ios >>= putStrLn
并像 printIOString getLine
让我们用几个更复杂的例子来热身。
main = do
x
x
x
putStrLn "foo"
where
x = do
getLine
您希望它做什么?我不了解你,但是 I 期望程序得到三行然后打印一些东西。如果我们对第二个 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"
这是你写的代码的脱糖。
根据https://whosebug.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 = f x
),而实际上
do { _ <- getLine ; putStrLn "foo" }
=
getLine >>= (\ _ -> putStrLn "foo")
无法进一步简化。