2012-03-22 45 views
10

我认为,原则上haskell的类型系统将禁止从纯粹的函数中调用不纯的函数(即f :: a -> IO b),但今天我意识到通过return来调用它们,它们编译得很好。在例如:现在纯函数中IO操作的含义是什么?

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

h作品中,也许单子,但它是一个纯函数不过。编译并运行它只需返回Just(),而不需要实际执行任何I/O操作。我认为哈斯克尔的懒惰把东西放在一起(即putStrLn的返回值没有被使用 - 因为它的值构造函数是隐藏的,我无法对它进行模式匹配),但为什么这个代码是合法的呢?是否有任何其他原因使得这个允许?

作为一个奖励,相关问题:一般来说,是否有可能从其他内部禁止monad的所有操作?怎么样?

回答

19

IO操作是像任何其他操作一样的一流值;这就是Haskell的IO如此表达的原因,让您可以从头构建更高阶的控制结构(如mapM_)。懒惰在这里不相关,这只是你实际上并不是执行的动作。你只是在构建价值Just (putStrLn "???"),然后把它扔掉。

putStrLn "???"现有不会将行打印到屏幕上。本身,putStrLn "???"只是一个描述的一些IO可以做到使一行被打印到屏幕上。发生的唯一执行是执行main,这是您从其他IO操作构建的,或者您键入GHCi的任何操作。有关更多信息,请参见introduction to IO

事实上,完全可以想象你可能想在Maybe内忙于约IO动作;设想一个函数String -> Maybe (IO()),它检查字符串的有效性,如果有效,则返回IO操作以打印从字符串派生的一些信息。这可能恰恰是因为Haskell的一流IO操作。

但是一个monad无法执行另一个monad的动作,除非你给它这个能力。

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

+0

如何让monad从另一个执行动作的能力,通过赋予它与包含的值进行模式匹配的可能性? – 2012-03-22 09:46:17

+3

通过编写将一个monad转换为另一个monad的方法,或者执行一些操作。例如,Control.Monad.ST.stToIO'将'ST'计算转换为'IO'计算。 – 2012-03-22 10:08:02

+0

晶莹剔透。谢谢你们俩! – 2012-03-22 10:33:23

4

让我们开始吧!现在

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会发生什么?*嗯,也许吧,

(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。有些不清楚“desugaring”停止和“评估”开始的时间点。这取决于您的编译器内联决策。纯粹的计算可以在编译时完全评估。

+1

感谢您的解职。对新人非常有用。我不明白为什么这么多的教程开始与糖。首先有晚餐,然后沙漠。 – masterxilo 2017-03-14 21:14:35

相关问题