懒惰评估和IO副作用混淆

The*_*kle 6 haskell functional-programming side-effects lazy-evaluation

此代码(取自Learn You A Haskell):

main = do   putStr "Hey, "  
            putStr "I'm "  
            putStrLn "Andy!"  
Run Code Online (Sandbox Code Playgroud)

显然是个傻瓜

main =        putStr "Hey, " >>=  
       (\_ -> putStr "I'm "  >>= 
       (\_ -> putStrLn "Andy!"))
Run Code Online (Sandbox Code Playgroud)

其中,正如我所理解的那样可以解释为"为了放入斯特伦"安迪!"我首先要把putStr"我是",为了做到这一点,我首先要把putStr"嘿,";

我不同意这种解释,这很烦人,因为编译器显然不会让我感到困惑.我遇到的问题是,lambdas忽略了他们的论点,在懒惰的评估期间,这种事情是不是应该被识别和短路?

此外,当然,绑定会返回IO操作,当IO操作进入main时,它会被执行.但是什么阻止它打印"嘿,安迪!我是"?我怀疑这是绑定正在做什么.

此外,类型"IO()"的IO操作如何携带足够的信息以允许运行时系统打印"嘿,我是安迪!"?IO()与IO()的不同之处在于打印"Hello World!" 或写入文件?

考虑另一个,来自monad的维基百科页面:

加糖版:

do
  putStrLn "What is your name?"
  name <- getLine
  putStrLn ("Nice to meet you, " ++ name ++ "!")
Run Code Online (Sandbox Code Playgroud)

Desugared版本:

putStrLn "What is your name?" >>= 
   (\_ ->
      getLine >>=
         (\name ->
            putStrLn ("Nice to meet you, " ++ name ++ "!")))
Run Code Online (Sandbox Code Playgroud)

类似的故事在这里

我想我只需要看看bind for IO的定义然后就会清楚了.如果有人可以帮助我逐步完成程序的实际评估,并确定副作用发生的确切时刻,那么其他有用的东西就会有所帮助.

npo*_*cop 10

阅读Simon Peyton Jones 撰写的" 解决尴尬小队 "一文.

有关相关问题,请参阅

采取任何这样的解释,包括我的一粒盐 - 没有挥手可以取代严格的同行评审论文,解释必然过度简化.

一个非常粗略的视角是>>=可以看作列表构造函数:

data IO = [Primitive] 
Run Code Online (Sandbox Code Playgroud)

和IO子系统解构该main列表的值并使用该列表.即"主要is just a list. So you may want to take a look at the definition of Haskell entry point above主要,绑定"是相当无趣的.

您还可以阅读有关haskell历史的论文,并查看早期版本的IO子系统,以了解正在发生的事情.

另外看看C语言是 Conal Elliott 纯粹功能性的讽刺文章.

功能纯度的定义是非常重要的,我记得有一篇论文详细阐述了定义,但我不记得标题.

  • `IO`类型是抽象的,因此Haskell标准中没有`>> =`的定义.根据你实现`IO`的方式,你将有不同的`>> =`的实现.如果你深入研究ghc,你会发现`IO`是一个状态monad,而`>> =`只是一个状态monad的绑定.(编译器内部有更多魔法可以提高效率.) (5认同)

aug*_*tss 7

看着IO在真实Haskell的实现可能会混淆超过它启发.但是想到这样的IO定义(假设你知道GADT):

data IO a where
    Return a :: IO a
    Bind :: IO a -> (a -> IO b) -> IO b
    PutStr :: String -> IO ()
    GetLine :: IO String

instance Monad IO where
    return = Return
    (>>=) = Bind

putStr :: String -> IO ()
putStr = PutStr

getLine :: IO String
getLine = GetLine
Run Code Online (Sandbox Code Playgroud)

因此,当您评估程序(类型IO ())时,它所做的只是构建一个类型的数据结构,IO ()该结构描述了一旦执行它与世界的交互将如何发生.然后,您可以想象正在编写的执行引擎,例如C,并且存在所有效果发生的地方.

所以

main = do   putStr "Hey, "  
            putStr "I'm "  
            putStrLn "Andy!"  
Run Code Online (Sandbox Code Playgroud)

是相同的

main = Bind (PutStr "Hey, ") (\ _ -> Bind (PutStr "I'm ") (\ _ -> PutStr "Andy!"))
Run Code Online (Sandbox Code Playgroud)

它们的排序来自执行引擎的工作方式.

也就是说,我知道没有Haskell实现实际上是这样做的.真正的实现往往会落实IO与代表现实世界正在传递令牌的状态单子(这是保证测序),和原语一样putStr只是调用C函数.