我认为原则上haskell的类型系统会禁止f :: a -> IO b从纯粹的函数中调用不纯的函数(即),但今天我意识到通过调用它们return它们编译得很好.例如:
h :: Maybe ()
h = do
return $ putStrLn "???"
return ()
Run Code Online (Sandbox Code Playgroud)
现在,h可能在monad中工作,但它仍然是一个纯粹的功能.编译和运行它只是Just ()按照预期返回,而不实际执行任何I/O. 我认为haskell的懒惰将事物放在一起(即putStrLn不使用返回值 - 并且不能因为它的值构造函数被隐藏而且我无法模拟匹配它),但为什么这个代码合法?还有其他原因可以让这个允许吗?
作为奖励,相关问题:一般来说,是否有可能禁止monad的行为从其他行为中执行?怎么样?
ehi*_*ird 19
IO动作与其他动作一样是一流的值; 这就是让Haskell的IO如此富有表现力的原因,允许您mapM_从头开始构建更高阶的控制结构(如).懒惰与此无关,1只是你实际上没有执行动作.你只是在构建价值Just (putStrLn "???"),然后扔掉它.
putStrLn "???" 现有的不会导致行打印到屏幕上.就其本身而言,putStrLn "???"只是对某些IO 的描述,可以将行打印到屏幕上.发生的唯一执行是执行main,您从其他IO操作或您在GHCi中键入的任何操作构建.有关更多信息,请参阅IO简介.
事实上,完全可以想象你可能想要IO处理内部的行为Maybe; 想象一个函数String -> Maybe (IO ()),它检查字符串的有效性,如果它有效,则返回一个IO动作来打印从字符串派生的一些信息.这可能正是因为Haskell的一流IO操作.
但是monad没有能力执行另一个monad的行动,除非你赋予它那个能力.
1实际上,h = putStrLn "???" `seq` return ()即使它强制进行评估,也不会导致任何IO执行putStrLn "???".
让我们脱糖!
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 ())
Run Code Online (Sandbox Code Playgroud)
现在,当你评估时会发生什么h?*好吧,对于Maybe,
(Just x) >>= f = f x
Nothing >>= f = Nothing
Run Code Online (Sandbox Code Playgroud)
所以我们模式匹配第一种情况
f x
-- x = (putStrLn "???"), f = (\_ -> Just ())
(\_ -> Just ()) (putStrLn "???")
-- apply the argument and ignore it
Just ()
Run Code Online (Sandbox Code Playgroud)
请注意,我们无需执行putStrLn "???"即可评估此表达式。
*nb 目前尚不清楚“脱糖”何时停止而“评估”何时开始。这取决于编译器的内联决定。纯计算可以在编译时完全评估。
| 归档时间: |
|
| 查看次数: |
979 次 |
| 最近记录: |