【问题标题】:What's the meaning of IO actions within pure functions?纯函数中的 IO 动作是什么意思?
【发布时间】:2012-03-22 09:35:47
【问题描述】:

我认为原则上 Haskell 的类型系统会禁止从纯函数调用不纯函数(即f :: a -> IO b),但今天我意识到通过使用return 调用它们,它们编译得很好。在这个例子中:

h :: Maybe ()
h = do
    return $ putStrLn "???"
    return ()

hMaybe monad 中工作,但它仍然是一个纯函数。编译和运行它只返回Just (),正如人们所期望的那样,实际上没有做任何I/O。我认为 Haskell 的懒惰将这些东西放在一起(即 putStrLn 的返回值没有被使用 - 并且不能因为它的值构造函数被隐藏并且我无法对其进行模式匹配),但为什么这段代码是合法的?还有其他原因允许这样做吗?

作为奖励,相关问题:一般来说,是否可以完全禁止从其他单子中执行单子的操作,以及如何?

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    IO 操作和其他操作一样是一等值;这就是让 Haskell 的 IO 如此富有表现力的原因,它允许您从头开始构建高阶控制结构(如 mapM_)。懒惰在这里无关紧要,1只是你实际上并没有执行该动作。您只是在构造值 Just (putStrLn "???"),然后将其丢弃。

    putStrLn "???" existing 不会导致在屏幕上打印一行。就其本身而言,putStrLn "???" 只是一些 IO 的描述,可以用来将一行打印到屏幕上。唯一发生的执行是执行 main,它是您从其他 IO 操作或您在 GHCi 中键入的任何操作构建的。如需更多信息,请参阅introduction to IO

    确实,您完全可以想象在Maybe 中处理IO 操作;想象一个函数String -> Maybe (IO ()),它检查字符串的有效性,如果有效,则返回一个 IO 操作以打印从字符串派生的一些信息。正是因为 Haskell 一流的 IO 操作,这才成为可能。

    但是一个monad没有能力执行另一个monad的动作,除非你给它那个能力。

    1 事实上,h = putStrLn "???" `seq` return () 也不会导致任何 IO 被执行,即使它强制评估 putStrLn "???"

    【讨论】:

    • 我怎样才能让一个monad能够执行另一个monad的动作,通过给它模式匹配它包含的值的可能性?
    • 通过编写将一个 monad 转换为另一个 monad 的方法,或者执行一些操作。例如,Control.Monad.ST.stToIOST 计算转换为 IO 计算。
    • 感谢“只是对 io 的描述”。这解决了我的 io 谜团,甚至不用关心 monad。
    • 感谢您的回答。我想知道像 f :: (Int -> IO ()) -> Int 这样的函数会发生什么
    【解决方案2】:

    让我们脱糖吧!

    h = do return (putStrLn "???"); return ()
    -- rewrite (do foo; bar) as (foo >> do bar)
    h = return (putStrLn "???") >> do return ()
    -- redundant do
    h = return (putStrLn "???") >> return ()
    -- return for Maybe = Just
    h = Just (putStrLn "???") >> Just ()
    -- replace (foo >> bar) with its definition, (foo >>= (\_ -> bar))
    h = Just (putStrLn "???") >>= (\_ -> Just ())
    

    现在,当您评估 h 时会发生什么?* 好吧,对于 Maybe,

    (Just x) >>= f = f x
    Nothing  >>= f = Nothing
    

    所以我们模式匹配第一种情况

    f x
    -- x = (putStrLn "???"), f = (\_ -> Just ())
    (\_ -> Just ()) (putStrLn "???")
    -- apply the argument and ignore it
    Just ()
    

    请注意,我们无需执行 putStrLn "???" 即可评估此表达式。

    *n.b.目前还不清楚“脱糖”何时停止而“评估”何时开始。这取决于您的编译器的内联决定。纯计算可以完全在编译时进行评估。

    【讨论】:

    • 感谢您的脱糖。对新手非常有用。我不明白为什么这么多教程都是从加糖开始的。先吃晚饭,再吃甜点。
    猜你喜欢
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 2015-09-25
    • 1970-01-01
    • 2017-10-20
    相关资源
    最近更新 更多