在玩monads时,我经常遇到评估问题。现在,我理解了惰性计算的基本概念,但我不明白在Haskell中是如何懒惰地计算monads的。
考虑以下代码
module Main where
import Control.Monad
import Control.Applicative
import System
main = print <$> head <$> getArgs在我看来,main函数应该打印第一个控制台参数,但它没有。
我知道
getArgs :: IO [String]
head <$> getArgs :: IO String
print <$> (head <$> getArgs) :: IO (IO ())
main :: IO (IO ())显然,第一个参数没有打印在stdout上,因为第一个monad IO的内容没有求值。因此,如果我加入这两个monads,它就可以工作。
main = join $ print <$> head <$> getArgs有没有人能帮我澄清一下?(或者给我一个指针)
发布于 2011-10-17 21:04:37
Haskell 2010报告(语言定义) says
程序的值是模块
Main中标识符main的值,对于某些类型的τ,它必须是IO τ类型的计算。当执行程序时,执行计算main,并丢弃其结果(类型为τ)。
您的main函数的类型为IO (IO ())。上面的引号表示只评估外部操作(IO (IO ())),并丢弃其结果(IO ())。你是怎么到这里来的?让我们来看看print <$>的类型
> :t (print <$>)
(print <$>) :: (Show a, Functor f) => f a -> f (IO ())所以问题是你把fmap和print结合使用了。查看IO的Functor实例的定义
instance Functor IO where
fmap f x = x >>= (return . f)您可以看到,这使得您的表达式等同于(head <$> getArgs >>= return . print)。要实现最初的目的,只需删除不必要的return
head <$> getArgs >>= print或者,等效地:
print =<< head <$> getArgs请注意,Haskell中的IO操作就像其他值一样-它们可以传递给函数或从函数返回,存储在列表和其他数据结构中,等等。IO操作不会计算,除非它是主计算的一部分。要将IO操作“粘合”在一起,请使用>>和>>=,而不是fmap (它通常用于将纯函数映射到某些“框”中的值-在您的示例中是IO)。
还要注意,这与惰性计算无关,而是纯正的--从语义上讲,您的程序是一个纯函数,它返回一个IO a类型的值,然后由运行时系统解释。由于您内部的IO操作不是此计算的一部分,因此运行时系统将其丢弃。关于这些问题的一个很好的介绍是Simon Peyton Jones的"Tackling the Awkward Squad"的第二章。
发布于 2011-10-17 21:05:44
你有head <$> getArgs :: IO String和print :: Show a => a -> IO (),即monad中的值和从普通值到monad的函数。用于组合这些内容的函数是一元绑定运算符(>>=) :: Monad m => m a -> (a -> m b) -> m b。
所以你想要的是
main = head <$> getArgs >>= print(<$>)又名fmap,它的类型是Functor f => (a -> b) -> f a -> f b,所以当你想将一个纯函数应用于monad中的某个值时,它很有用,这就是为什么它可以与head一起使用,但不能与print一起使用,因为print不是纯函数。
https://stackoverflow.com/questions/7793981
复制相似问题