Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么getLine的值不是必需的,却要对其求值?_Haskell - Fatal编程技术网

Haskell 为什么getLine的值不是必需的,却要对其求值?

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 >>= (

如果Haskell是懒惰的,为什么在以下两种情况下都要计算
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"