[英]How infinite loop with IO work in haskell
在haskell中,我們編寫的所有IO代碼都只是一個動作(許多建議將其視為正在生成的腳本)。 它是最終執行它們(執行構造的腳本)的主要方法。 那么以下程序如何工作? infi
函數將永遠不會返回。 那么,為什么字符串會無限打印呢?
infi =
do
print "hello"
infi
main =
do
infi
您似乎對Haskell如何實際實現IO產生了誤解。 這個主題上還有很多其他文獻,與此站點有關的其他答案也很多,因此,我將重點介紹您的具體示例,而不是一般示例。
infi =
do
print "hello"
infi
main =
do
infi
首先,您可以簡化您的主要動作(無需do
):
main = infi
在haskell中,“返回”與命令式“返回”不同。 它只是意味着注入單子動作,即return :: Monad m => a -> ma
。 因此,讓我們談談這里評估的是什么,而不是什么時候返回。
您所做的所有main
功能就是調用infi :: IO ()
類型的值infi
。 由於infi
是IO
操作,因此可以執行打印。 像任何其他值一樣,它也可以引用其他值(在這種情況下,它是遞歸的,因此可以調用自身)。 沒有基本情況,INFI將繼續執行以下序列(就像在您的do
塊中所示!):
之所以可行,主要是因為Haskell的懶惰評估。 Haskell永遠不會真正計算值,直到您需要它為止。 這就是為什么您也可以對無限列表執行純操作:
let x = [1..] -- x is an infinite list. If you told haskell to print every element, it would run forever since it would evaluate the whole thing.
let y = x !! 3 -- y = 2. This is not infinite because you are only evaluating the first three elements, instead of the whole value.
您的無限值infi
也是如此。 Haskell可以創建一個包含無限操作的“運行時腳本”,因為infi的值具有有限的表示形式(其名稱),但是由於沒有基本情況,因此它的計算結果是無限的。
infi
是基本上相同的無限流IO
動作,與一元測序(連接>>
)代替缺點( :
):
fives = 5 : fives
infi = putStrLn "hello" >> infi
實際上,我們可以使用一系列infi'
動作抽象出monadic綁定:
infi' :: [IO ()]
infi' = putStrLn "hello" : infi'
然后使用sequence_
恢復infi
,該sequence_
可以實現為foldr (>>) (return ())
。
infi = sequence_ infi'
infi = (foldr (>>) (return ()) infi')
infi = putStrLn "hello" >> (foldr (>>) (return ()) infi')
infi = putStrLn "hello" >> (putStrLn "hello" >> (foldr (>>) (return ()) infi'))
infi = putStrLn "hello" >> (putStrLn "hello" >> (putStrLn "hello" >> ...))
將動作存儲在這樣的流中還可以讓您將它們作為一流的值進行操作:
> sequence_ (take 3 infi')
hello
hello
hello
當Haskell運行時執行您的main
操作時,它將計算infi
,找到>>
表達式,計算其左手參數以產生操作putStrLn "hello"
, 執行該操作,然后繼續執行右手參數-再次成為infi
。 評估由IO
的Monad
實例中的內部模式匹配懶惰地驅動。
它與程序非常相似
ones = 1 : ones
上面是遞歸的,是的。 它會無限多次調用自己,是的。 但是它確實會返回。 它返回一個無限列表。 通過對比,
noList = noList
將永遠循環而不會返回列表。 (實際上,GHC運行時會檢測到此情況並引發異常,但這與討論無關。)
同樣,
printOnes = print 1 >> printOnes
-- or, equivalently
printOnes = do
print 1
printOnes
建立一個IO操作,該操作將永遠打印1
,即使它無限次遞歸多次也是如此。 代替,
noPrint = noPrint
會永遠循環並且永遠不會返回IO操作。
因為將infi設置為遞歸調用其自身。 從main預期的“返回”將永遠不會實現,因為對infi的首次調用將永遠不會返回。
# The first call to infi will never return as the calls to infi
# will just continue to add more calls to the stack, until you exceed
# the size :)
main ->
infi ->
infi ->
infi -> ..
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.