Jac*_*ack 8 monads haskell lazy-evaluation
在玩monad时,我经常会遇到评估问题.现在,我理解了懒惰评估的基本概念,但我不知道在Haskell中如何懒惰地评估monad.
请考虑以下代码
module Main where
import Control.Monad
import Control.Applicative
import System
main = print <$> head <$> getArgs
Run Code Online (Sandbox Code Playgroud)
在我看来,主函数应该打印第一个控制台参数,但事实并非如此.
我知道
getArgs :: IO [String]
head <$> getArgs :: IO String
print <$> (head <$> getArgs) :: IO (IO ())
main :: IO (IO ())
Run Code Online (Sandbox Code Playgroud)
显然,第一个参数没有打印在stdout上,因为第一个monad IO的内容没有被评估.所以如果我加入两个monad,它就可以了.
main = join $ print <$> head <$> getArgs
Run Code Online (Sandbox Code Playgroud)
有人请你为我澄清一下吗?(或给我一个指针)
Mik*_*kov 11
Haskell 2010报告(语言定义)说:
程序的值是
main模块中 标识符的值Main,它必须是IO ?某种类型的类型计算?.当程序执行时,执行计算main,并?丢弃其结果(类型).
你的main功能有类型IO (IO ()).上面的引用意味着只IO (IO ())评估外部action(),并IO ()丢弃其result().你是怎么来到这里?我们来看看类型print <$>:
> :t (print <$>)
(print <$>) :: (Show a, Functor f) => f a -> f (IO ())
Run Code Online (Sandbox Code Playgroud)
所以问题在于你fmap与之配合使用print.查看Functor实例的定义IO:
instance Functor IO where
fmap f x = x >>= (return . f)
Run Code Online (Sandbox Code Playgroud)
你可以看到那使你的表达相当于(head <$> getArgs >>= return . print).要做你原来想要的,只需删除不必要的return:
head <$> getArgs >>= print
Run Code Online (Sandbox Code Playgroud)
或者,等效地:
print =<< head <$> getArgs
Run Code Online (Sandbox Code Playgroud)
请注意,Haskell中的IO操作就像其他值一样 - 它们可以传递给函数并从函数返回,存储在列表和其他数据结构中等.除非它是主计算的一部分,否则不会评估IO操作.要将IO操作"粘合"在一起,请使用>>and >>=,而不是fmap(通常用于将纯函数映射到某些"框"中的值 - 在您的情况下IO).
另请注意,这不是懒惰评估,而是纯度 - 从语义上讲,您的程序是一个纯函数,它返回一个类型的值,IO a然后由运行时系统解释.由于您的内部IO操作不是此计算的一部分,因此运行时系统只会将其丢弃.这些问题的一个很好的介绍是西蒙佩顿琼斯的第二章"解决尴尬的小队".