を書くとき[1..1000000]
、GHCが実際に行うことは、関心のあるリストを作成する方法を含み1
、1000000
それを説明するオブジェクトを作成することです。そのオブジェクトは「サンク」と呼ばれます。このリストは、ケースの精査者を満足させるために必要な場合にのみ作成されます。たとえば、次のように書くことができます。
printList [] = putStrLn ""
printList (x:xs) = putStrLn (show x) >> printList xs
main = printList [1..1000000]
これは次のように評価されます:
main
= { definition of main }
printList [1..1000000]
= { list syntax sugar }
printList (enumFromTo 1 1000000)
= { definition of printList }
case enumFromTo 1 1000000 of
[] -> putStrLn ""
x:xs -> putStrLn (show x) >> printList xs
= { we have a case, so must start evaluating enumFromTo;
I'm going to skip a few steps here involving unfolding
the definition of enumFromTo and doing some pattern
matching }
case 1 : enumFromTo 2 1000000 of
[] -> putStrLn ""
x:xs -> putStrLn (show x) >> printList xs
= { now we know which pattern to choose }
putStrLn (show 1) >> printList (enumFromTo 2 1000000)
次に、それがコンソールに印刷されていることがわかります。1
上部の近くから、のenumFromTo 2 1000000
代わりにを使用して開始しenumFromTo 1 1000000
ます。最終的には、すべての数値が印刷され、評価する時期が来るでしょう。
printList (enumFromTo 1000000 1000000)
= { definition of printList }
case enumFromTo 1000000 1000000 of
[] -> putStrLn ""
x:xs -> putStrLn (show x) >> printList xs
= { skipping steps again to evaluate enumFromTo }
case [] of
[] -> putStrLn ""
x:xs -> putStrLn (show x) >> printList xs
= { now we know which pattern to pick }
putStrLn ""
そして評価は終了します。
ストリームが必要な理由は少し微妙です。元の論文「ストリームの融合:リストからストリーム、そして何もなしまで」には、おそらく最も完全な説明があります。短いバージョンは、長いパイプラインがある場合です。
concatMap foo . map bar . filter pred . break isSpecial
...コンパイラにすべての中間リストをコンパイルさせる方法はそれほど明白ではありません。リストは、反復されている一種の「状態」を持っていると考えることができ、これらの各関数は、リストをトラバースするのではなく、反復ごとに状態が変更される方法を変更しているだけです。タイプはStream
これを明示的にしようとし、結果はストリーム融合です。外観は次のとおりです。まず、これらすべての関数をストリームバージョンに変換します。
(toList . S.concatMap foo . fromList) .
(toList . S.map bar . fromList) .
(toList . S.filter pred . fromList) .
(toList . S.break isSpecial . fromList)
次に、私たちがいつでも全滅させることができることを観察してfromList . toList
ください:
toList . S.concatMap foo . S.map bar . S.filter pred . S.break . fromList
...そして、チェーンがS.concatMap foo . S.map bar . S.filter pred . S.break
イテレータを内部的に構築してすぐに実際のリストを消滅させることによって暗黙的に構築するのではなく、明示的に構築するため、魔法が起こります。