Haskell 为什么getLine的值不是必需的,却要对其求值?
如果Haskell是懒惰的,为什么在以下两种情况下都要计算Haskell 为什么getLine的值不是必需的,却要对其求值?,haskell,Haskell,如果Haskell是懒惰的,为什么在以下两种情况下都要计算getLine?由于懒惰,我希望在fa的情况下,getLine不会被评估,因为它的结果随后没有被使用: let fa = do { x <- getLine; return "hello" } let fb = do { x <- getLine; return $ x } 让fa=do{ x它的结果正在被使用,只是没有以你必然期望的方式使用。这会使糖分分解 fa = getLine >>= (
getLine
?由于懒惰,我希望在fa
的情况下,getLine
不会被评估,因为它的结果随后没有被使用:
let fa = do {
x <- getLine;
return "hello"
}
let fb = do {
x <- getLine;
return $ x
}
让fa=do{
x它的结果正在被使用,只是没有以你必然期望的方式使用。这会使糖分分解
fa = getLine >>= (\x -> return "hello")
因此,getLine
的结果仍然会传递给函数\x->return“hello”
。monad本质上是将动作排序在一起(除其他外);即使以后不使用结果,排序仍然会发生。如果不是这样,那么
main = do
print "Hello"
print "World"
作为一个程序不会做任何事情,因为对print
的两个调用的结果都没有被使用。祝贺你,你刚刚发现了为什么Haskell是一种纯函数式语言
getLine
的结果†不是一个字符串。它是一个IO操作,碰巧“生成”了一个字符串。该字符串确实没有被计算,但该操作本身是(因为它出现在绑定到main
的do
块中),就副作用而言,这就是最重要的
†实际上只是getLine
的值。这不是一个函数,因此实际上没有结果。现在要小心…;-)
如果您愿意的话,getLine
的结果不是字符串,而是一个“I/O命令对象”
getLine >>= (\ x -> return "hello")
>=
操作符从现有的I/O命令对象和函数中构造一个新的I/O命令对象……好吧,这有点让人费解。重要的是,I/O操作会被执行(因为>=
对于IO
),但其结果不一定会得到评估(因为懒惰)
因此,让我们看看IO
monad的实现……呃,实际上你知道吗?让我们不要。(这是一种很深的魔法,硬连接到编译器中,因此它是特定于实现的。)但这一现象无论如何都不是IO
所独有的。让我们看看可能monad:
instance Monad Maybe where
mx >>= f =
case mx of
Nothing -> Nothing
Just x -> f x
return x = Just x
所以如果我做类似的事情
do
x <- foobar
return "hello"
然后这就变成了
case foobar of
Nothing -> Nothing
Just x -> Just "hello"
正如您所看到的,foobar
显然将被评估,因为我们需要知道结果是Nothing
还是Just
。但是实际的x
不会被评估,因为没有任何东西会查看它
这与length
计算列表节点的方式类似,但不是计算它们指向的列表元素。不要在上面加太细的一点,但这里的要点是x
(而不是getLine
!)是惰性计算允许您避免计算的东西。您可以通过执行do来验证这一点{x>=
)@DanielWagner那么,IO monad中是否有一些内部定义,强制对双方的操作进行评估?我试图找到这个实现,但没有。我在哪里可以看到它?@mljrg小心:深魔法!@DanielWagner好的,我知道它使用的是状态monad,但我怀疑ccall
构造是alw执行的日期,无论其值是否在后续操作中使用。或..或..每个IO操作返回并使用的(World,a)
强制执行所有涉及副作用的IO操作,无论其值a
是否在后续操作中使用(只是猜测)@mljrg它没有使用标准的状态
monad,但它使用的是类似的东西。你是对的,运行时系统中有一个部分必须与显然(但表面上是虚假的)纯Haskell实现合作;这通常通过将“执行”与“评估”分开来描述谢谢你的回答,但我有两个问题要问。第一,当你去死foobar>=(\x->返回“hello”)
它去死…只是x->返回“hello”
不是吗?第二,我不怕“深魔法”,那么,你能向我展示一下IO单子中>=
实现背后的魔力吗,这样我们就可以用一种完整而有用的方式来解决这个堆栈溢出问题,让其他人来回答?谢谢。是的,它是Just x->return“hello”
。然后,从return
的定义中,我们得到Just x->Just“hello”
。我的观点是,这种效果与神奇的I/O底层实现无关(在GHC版本之间有所不同);它在大多数Monad中都很常见,包括可能
。您说过“神奇的I/O底层实现(在GHC版本之间有所不同)”。我希望这种情况下,如果要在后续版本中改进实现,保持相同的行为,IO
monad的行为在Haskell报告中指定,并且不会更改。该实现根本没有指定,可以是任何GHC(或任何其他编译器)这个答案不完整;请看@MathematicalArchid的答案。
case foobar of
Nothing -> Nothing
Just x -> Just "hello"