まずmain
、関数ではありません。それは確かに単なる通常の値であり、その型はIO ()
です。タイプは次のように読むことができます: 実行されると type の値を生成するアクション()
。
これで、ランタイム システムは、説明したアクションを実行するインタープリターの役割を果たします。あなたのプログラムを例に取りましょう:
main = forever (putStrLn "Hello world!")
変換を実行したことに注意してください。Haskell は参照透過型言語であるため、これは有効です。ランタイム システムは を解決し、forever
これを見つけます。
main = putStrLn "Hello world!" >> MORE1
何が何であるかはまだわかりませんMORE1
が、実行される 1 つの既知のアクションを含むコンポジションがあることがわかりました。実行後、2 番目のアクションを解決し、MORE1
以下を見つけます。
MORE1 = putStrLn "Hello world!" >> MORE2
再び、そのコンポジションの最初のアクションを実行し、解決を続けます。
もちろん、これは高度な説明です。実際のコードはインタープリターではありません。しかし、これは Haskell プログラムがどのように実行されるかを示す方法です。別の例を見てみましょう:
main = forever (getLine >>= putStrLn)
RTS はこれを見ます:
main = forever MORE1
<< resolving forever >>
MORE1 = getLine >>= MORE2
<< executing getLine >>
MORE2 result = putStrLn result >> MORE1
<< executing putStrLn result (where 'result' is the line read)
and starting over >>
これを理解するIO String
と、 が「副作用のある文字列」ではなく、文字列を生成するアクションの説明であることがわかります。また、Haskell の I/O システムが機能するために遅延が重要である理由も理解できます。