Haskell 和惰性 Monads 评估
在使用 monad 时,我经常遇到评估问题。现在,我了解了延迟求值的基本概念,但我不明白如何在 Haskell 中延迟求值 monad。
考虑以下代码
module Main where
import Control.Monad
import Control.Applicative
import System
main = print <$> head <$> getArgs
在我看来,主函数应该打印第一个控制台参数,但事实并非如此。
我知道
getArgs :: IO [String]
head <$> getArgs :: IO String
print <$> (head <$> getArgs) :: IO (IO ())
main :: IO (IO ())
很明显,第一个参数没有打印在标准输出上,因为第一个 monad IO 的内容没有被评估。所以如果我加入这两个单子,它就会起作用。
main = join $ print <$> head <$> getArgs
有人可以帮我澄清一下吗? (或者给我指点)
While playing with monads I often incur in problems of evaluation. Now, I understand the basic concepts of lazy evaluation, but I don't get how monads are lazily evaluated in Haskell.
Consider the following code
module Main where
import Control.Monad
import Control.Applicative
import System
main = print <gt; head <gt; getArgs
In my mind it should the main function should print the first console argument, but it doesn't.
I know that
getArgs :: IO [String]
head <gt; getArgs :: IO String
print <gt; (head <gt; getArgs) :: IO (IO ())
main :: IO (IO ())
so apparently, the first argument is not printed on the stdout because the content of the first monad IO is not evaluated. So if I join the two monads, it works.
main = join $ print <gt; head <gt; getArgs
Would anyone, please, clarify it for me? (or give me a pointer)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Haskell 2010 报告(语言定义)说:
您的
main
函数的类型为IO (IO ())
。上面的引用意味着仅评估外部操作 (IO (IO ())
),并丢弃其结果 (IO ()
)。你是怎么到这里来的?让我们看看print <$>
的类型:所以问题是您将
fmap
与print
结合使用。查看IO
的Functor
实例的定义:您可以看到,这使得您的表达式相当于
(head <$> getArgs >>=返回。要执行您最初的意图,只需删除不必要的
return
:或者,等效地:
请注意,Haskell 中的 IO 操作就像其他值一样 - 它们可以传递给函数并从函数返回,存储在列表中,其他数据结构等。除非 IO 操作是主计算的一部分,否则不会对其进行评估。要将 IO 操作“粘合”在一起,请使用
>>
和>>=
,而不是fmap
(通常用于映射pure 函数作用于某些“盒子”中的值 - 在您的情况下,IO
)。另请注意,这与惰性求值无关,而是与纯度有关 - 从语义上讲,您的程序是一个纯函数,它返回 IO a 类型的值,然后由运行时系统进行解释。由于您的内部 IO 操作不是此计算的一部分,因此运行时系统只会丢弃它。 Simon Peyton Jones 的第二章对这些问题做了很好的介绍 “解决尴尬的小队”。
Haskell 2010 Report (the language definition) says:
Your
main
function has typeIO (IO ())
. The quote above means that only the outer action (IO (IO ())
) is evaluated, and its result (IO ()
) is discarded. How did you get here? Let's look at the type ofprint <$>
:So the problem is that you used
fmap
in conjunction withprint
. Looking at the definition ofFunctor
instance forIO
:you can see that that made your expression equivalent to
(head <$> getArgs >>= return . print)
. To do what you originally intended, just remove the unnecessaryreturn
:Or, equivalently:
Note that IO actions in Haskell are just like other values - they can be passed to and returned from functions, stored in lists and other data structures, etc. An IO action is not evaluated unless it's a part of the main computation. To "glue" IO actions together, use
>>
and>>=
, notfmap
(which is typically used for mapping pure functions over values in some "box" - in your case,IO
).Note also that this has to do not with lazy evaluation, but purity - semantically, your program is a pure function that returns a value of type
IO a
, which is then interpreted by the runtime system. Since your innerIO
action is not part of this computation, the runtime system just discards it. A nice introduction to these issues is the second chapter of Simon Peyton Jones's "Tackling the Awkward Squad".你有
head <$>; getArgs :: IO String
和print :: 显示 =>一个-> IO()
,即 monad 中的值以及从普通值到 monad 的函数。用于组成这些东西的函数是一元绑定运算符(>>=) :: Monad m =>妈-> (a→mb)→ m b
。所以你想要的是
(<$>)
又名fmap
具有类型Functor f =>; (a→b)→发-> f b
,因此当您想要将纯函数应用于 monad 中的某个值时,它非常有用,这就是为什么它适用于head
而不适用于 < code>print,因为print
不是纯粹的。You have
head <$> getArgs :: IO String
andprint :: Show a => a -> IO ()
, i.e. a value in a monad and a function from a plain value to a monad. The function used to compose such things is the monadic bind operator(>>=) :: Monad m => m a -> (a -> m b) -> m b
.So what you want is
(<$>)
akafmap
has the typeFunctor f => (a -> b) -> f a -> f b
, so it is useful when you want to apply a pure function to some value in a monad, which is why it works withhead
but not withprint
, sinceprint
is not pure.