2

Haskellを学んでいる間、IOアクションがいつ実行されるのか疑問に思っています。いくつかの場所で私はこのような説明を見つけました:

「I/Oアクションの特別な点は、それらがメイン機能に該当する場合、それらが実行されることです。」

ただし、次の例では、「greet」が返されることはないため、何も出力されません。

import Control.Monad

main = greet

greet = forever $ putStrLn "Hello World!"

または多分私は尋ねるべきです:「主な機能に陥る」とはどういう意味ですか?

4

5 に答える 5

11

まず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 システムが機能するために遅延が重要である理由も理解できます。

于 2012-09-01T13:32:28.300 に答える
5

私の意見では、「I/O アクションの特別な点は、メイン関数に分類された場合に実行されることです。」IO行動は第一級市民であるということです。つまり、IOアクションは、他のデータ型の値が発生する可能性があるすべての場所で発生する可能性がありIntます。たとえば、IOアクションを含むリストを次のように定義できます。

actionList = [putStr "Hello", putStr "World"]

リストのactionListタイプは[IO ()]です。つまり、リストには、コンソールに出力したり、ユーザーからの入力を読み取ったりするなど、世界と対話するアクションが含まれています。ただし、このリストを定義する際にアクションを実行するのではなく、後で使用するためにアクションをリストに入れるだけです。

IOプログラムのどこかで発生する可能性がある場合、これらのアクションが実行されたときに問題が発生し、ここで問題が発生しますmain。次の の定義を考えてみましょうmain

main = do
  actionList !! 0
  actionList !! 1

このmain関数は、リストの最初と 2 番目のコンポーネントに射影し、対応するアクションをその定義内で使用して「実行」します。mainアクションを実行するのは必ずしも関数自体である必要はないことに注意してくださいIO。関数から呼び出されるmain関数は、アクションも実行できます。たとえば、アクションを呼び出す関数を定義し、次のactionListようmainにこの関数を呼び出すことができます。

main = do
  caller
  putStr "!"

caller = do
  actionList !! 0
  actionList !! 1

単純な名前変更である必要がないことを強調するためにmain = caller、リストからアクションを実行した後に感嘆符を出力するアクションを追加しました。

于 2012-09-01T10:18:19.423 に答える
2

do 表記を使用して、単純な IO アクションをより高度なアクションに組み合わせることができます。

main = do
    printStrLn "Hello"
    printStrLn "World"

printStrLn "Hello"IO アクションとIO アクションを組み合わせprintStrLn "World"ます。Main は、最初に「Hello」という行を出力し、次に「World」という行を出力する IO アクションになりました。do 記法 (単なる構文上の砂糖) なしで書くと、次のようになります。

main = printStrLn "Hello" >> printStrLn "World"

>>ここでは、2 つのアクションを組み合わせた関数を確認できます。

行を読み取り、それを関数に渡す IO アクションを作成できます (これは素晴らしい処理を行います:))。結果は次のように出力されます。

main = do
    input <- getLine
    let result = doAwesomeStuff input
    printStrLn result

または結果を変数にバインドせずに:

main = do
    input <- getLine
    printStrLn (doAwesomeStuff input)

これはもちろん、次のようにそれらを組み合わせた IO アクションと関数として記述することもできます。

main = getLine >>= (\input -> printStrLn (doAwesomeStuff input))

プログラムを実行すると、メインの IO アクションが実行されます。これは、IO アクションが実際に実行される唯一の時間です。(技術的には、プログラム内でそれらを実行することもできますが、安全ではありません。これを行う関数は呼び出されunsafePerformIOます。)

ここで詳細を読むことができます: http://www.haskell.org/haskellwiki/Introduction_to_Haskell_IO/Actions

(このリンクはおそらく私の説明よりも優れていますが、ほぼすべてを書いた後に初めて見つけました。また、かなり長いです)

于 2012-09-01T13:32:04.640 に答える
1
launchAMissile :: IO ()
launchAMissile = do
    openASilo
    loadCoordinates
    launchAMissile

main = do
    let launch3missiles = launchAMissile >> launchAMissile >> launchAMissile
    putStrLn "Not actually launching any missiles"
于 2012-09-02T05:46:27.870 に答える