如果Haskell很懒,为什么在以下两种情况下都会评估getLine
?懒惰我会期望在fa
的情况下getLine
不会被评估,因为其结果不会被随后使用:
let fa = do {
x <- getLine;
return "hello"
}
let fb = do {
x <- getLine;
return $ x
}
(我在GHCi中测试了两个案例)
由于
答案 0 :(得分:4)
它的结果正在被使用,而不是你想象的方式。这消失了
fa = getLine >>= (\x -> return "hello")
因此getLine
的结果仍然传递给函数\x -> return "hello"
。 Monads本质上是关于一起排序行动(除此之外);即使以后没有使用结果,测序仍然会发生。如果不是这样,那么
main = do
print "Hello"
print "World"
不会作为一个程序执行任何操作,因为两次调用print
的结果都没有被使用。
答案 1 :(得分:2)
恭喜,您刚刚发现为什么Haskell是一种纯函数式语言!
getLine
的结果† 不一个字符串。这是一个IO动作恰好“生成”一个字符串。确实没有评估该字符串,但是操作本身是(因为它出现在绑定到do
的{{1}}块中),就副作用而言,这一切都很重要。< / p>
† 真的只是main
的值。这不是一个函数,因此它实际上没有结果。
答案 2 :(得分:2)
现在要小心......; - )
getLine
的结果不是字符串,它是“I / O命令对象”,如果你愿意的话。该代码实际上是去了
getLine >>= (\ x -> return "hello")
>>=
运算符从现有的I / O命令对象和函数中构造出一个新的I / O命令对象......好的,这有点让你大吃一惊。重要的是,执行I / O操作(因为>>=
的{{1}}的实现),但其结果不一定得到评估(因为懒惰)。
那么让我们来看看IO
monad ......的实现,其实你知道吗?我们不。 (这很神奇,很难连接到编译器中,因此它是特定于实现的。)但是这种现象并不是IO
所独有的。我们来看看IO
monad:
Maybe
所以,如果我做了像
这样的事情instance Monad Maybe where
mx >>= f =
case mx of
Nothing -> Nothing
Just x -> f x
return x = Just x
do
x <- foobar
return "hello"
会被评估吗?我们看看吧。它黯然失色:
x
然后这就变成了
foobar >>= (\ x -> return "hello")
如您所见,case foobar of
Nothing -> Nothing
Just x -> Just "hello"
显然会被评估,因为我们需要知道结果是foobar
还是Nothing
。但是实际的Just
不会被评估,因为没有任何东西可以查看它。
它与x
评估列表节点的方式相同,但不是它们指向的列表元素。