Haskell 如何“脱糖”? getline 在这个 do 块中?
我读过几本关于 Haskell 的书,但还没有编写太多代码,而且我对 Haskell 在某种情况下所做的事情有点困惑。假设我正在使用 getLine,以便用户可以按某个键继续,但我真的不想以任何有意义的方式解释该人的输入。我相信这是执行此操作的有效方法:
main = do
_ <- getLine
putStrLn "foo"
我了解其正在执行的操作的基本要点。 getLine
返回一个 IO String
,putStrLn
接受一个 String
并返回 IO ()
>,所以如果理论上我想打印用户在控制台中输入的内容,我基本上会使用 Monad
类中的 >>=
运算符。就我而言,我相信我的代码相当于 getLine >>> putStrLn "foo"
因为我放弃了 getLine
的返回值。
但是,如果我这样做怎么办?
main = do
let _ = getLine
putStrLn "foo"
在本例中,我们正在设置一种 lambda 来处理需要 IO String 的东西,对吗?我可以编写一个 printIOString 函数来打印用户的输入,这样就可以正常工作。然而,当我实际上没有使用该 IO String 时,程序的行为很奇怪...... getLine 甚至不提示我输入;程序只是打印出“foo”
。
我不太确定这里的“脱糖”语法是什么,或者这是否会揭示 Haskell 在幕后所做的事情。
I've read a few books on Haskell but haven't coded in it all that much, and I'm a little confused as to what Haskell is doing in a certain case. Let's say I'm using getLine
so the user can push a key to continue, but I don't really want to interpret that person's input in any meaningful way. I believe this is a valid way of doing this:
main = do
_ <- getLine
putStrLn "foo"
I understand the basic gist of what's this is doing. getLine
returns an IO String
, and putStrLn
takes a String
and returns IO ()
, so if I theoretically wanted to print what the user typed into the console, I'd basically utilize the >>=
operator from the Monad
class. In my case, I believe my code is equivalent to getLine >> putStrLn "foo"
since I'm discarding the return value of getLine
.
However, what if I do this instead?
main = do
let _ = getLine
putStrLn "foo"
In this case, we're setting up a sort of lambda to work with something that will take an IO String
, right? I could write a printIOString
function to print the user's input and that would work fine. When I'm not actually using that IO String
, though, the program behaves strangely... getLine
doesn't even prompt me for input; the program just prints out "foo"
.
I'm not really sure what the "desugared" syntax would be here, or if that would shed some light on what Haskell is doing under the hood.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
让我们用几个更复杂的例子来热身一下。
您希望这能起到什么作用?我不了解你的情况,但我期望程序得到三行,然后打印一些东西。如果我们对第二个
do
块进行脱糖,我们会得到因为这是对另一个块的脱糖,所以它的行为是相同的,在打印之前得到三行。如果您觉得第一个思路不直观,那么还有另一种思路可以得出相同的答案。 “引用透明性”是 Haskell 的定义特性之一,它的意思就是你可以用它的定义替换对某事物(即变量名)的“引用”,因此前面的程序应该与
我们的 程序完全相同。正在认真对待等式
x = getLine
。好的,我们有一个读取三行并打印的程序。这个呢?得到两行并打印。还有这个?
获取一行然后打印。希望您明白这是怎么回事...
获取零行然后打印,即立即打印!我使用
where
而不是let
来使开头的示例更加明显,但是您几乎总是可以将where
块替换为其 < code>let 表亲而不改变其含义:由于我们不引用
x
,我们甚至不需要命名它:这是您编写的代码的脱糖。
Let's warm up with a few more complicated examples.
What do you expect this to do? I don't know about you, but what I expect is for the program to get three lines and then print something. If we desugar the second
do
block, we getSince this is the desugaring of the other one, it behaves the same, getting three lines before printing. There's another line of thought that arrives at the same answer, if you don't find this first one intuitive. "Referential transparency", one of the defining features of Haskell, means exactly that you can replace a "reference" to something (that is, a variable name) with its definition, so the previous program should be exactly the same program as
if we are taking the equation
x = getLine
seriously. Okay, so we have a program that reads three lines and prints. What about this one?Get two lines and print. And this one?
Get one line and then print. Hopefully you see where this is going...
Get zero lines and then print, i.e. just print immediately! I used
where
instead oflet
to make the opening example a bit more obvious, but you can pretty much always replace awhere
block with itslet
cousin without changing its meaning:Since we don't refer to
x
, we don't even need to name it:and this is the desugaring of the code you wrote.
第一种情况按照您的预期脱糖:
相当于
在第二种情况下,
脱糖为
因为不需要
_ = getLine
值来计算let
表达式,编译器可以随意忽略它,并且永远不会执行 IO 效果,这就是为什么不再提示您进行 CLI 输入的原因。尽管这两种情况都忽略了
getLine
的结果,但区别在于第一种情况在IO
上下文中计算getLine
,而第二种情况则计算>getLine
作为纯值。在 IO 中,副作用必须一起执行和排序,但在纯上下文中,编译器可以自由地忽略未使用的值。我不建议这样做,因为它不是很惯用,但您可以编写类似的内容
并像
printIOString getLine
一样使用它The first case is desugared like you expected:
which is equivalent to
In the second case,
is desugared as
Since the
_ = getLine
value is not needed to evaluate the RHS of thelet
expression, the compiler is free to ignore it and the IO effect is never executed, which is why you're not prompted for CLI input anymore.Even though both cases ignored the result of
getLine
the difference is that the first case evaluatesgetLine
in anIO
context while the second case evaluatesgetLine
as a pure value. InIO
the side-effects must executed and sequenced together, but in a pure context the compiler is free to ignore unused values.I wouldn't recommend doing this as it's not very idiomatic, but you could write something like
and use it like
printIOString getLine
根据 https://stackoverflow.com/tags/do-notation/info,
Haskell 语义是等效的到
(使用
x & f = f x
),而实际上这不能进一步简化。
According to https://stackoverflow.com/tags/do-notation/info,
which by Haskell semantics is equivalent to
(with
x & f = f x
), whereas indeedwhich can't be further simplified.