2009-04-16 10 views
10

次のHaskellプログラムを考えてみましょう。私は関数がストリーム上で動作する "ストリームスタイル"(ここでは単にリストとして実装されています)でプログラムしようとしています。 normalStreamFuncのようなものは、怠惰なリストでうまくいく。私は無限のリストをnormalStreamFuncに渡し、無限の別のリストを効果的に出すことができますが、関数は各値にマップされます。 effectfulStreamFuncのようなものはうまく動作しません。 IOアクションは、個別の値を引き出す前にリスト全体を評価する必要があることを意味します。未評価の残りのアクションを残しIO効果を伴うHaskellストリーム

a 
b 
"[\"a\",\"b\"]" 

:プログラムはこれを生成するように

a 
b 
c 
d 
"[\"a\",\"b\"]" 

が、私がしたいことはeffectfulStreamFuncを書くための方法である:例えば、プログラムの出力がこれです。私はunsafePerformIOを使用して解決策を想像することができますが、私はテーブルからそれを取っているとしましょう。ここではプログラムは次のとおりです。

import IO 

normalStreamFunc :: [String] -> [String] 
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs 

effectfulStreamFunc :: [String] -> IO [String] 
effectfulStreamFunc [] = return [] 
effectfulStreamFunc (x:xs) = do 
    putStrLn x 
    rest <- effectfulStreamFunc xs 
    return (reverse(x):rest) 

main :: IO() 
main = do 
    let fns = ["a", "b", "c", "d"] 
    es <- effectfulStreamFunc fns 
    print $ show $ take 2 es 

更新:

が参考と思いやりのフィードバックありがとうございました。私は前にsequenceオペレータを見ていなかった、それについて知っておくと便利です。私はString(String)の代わりにIO(String)値を渡す方法はあまりエレガントではないと思っていましたが、限られた有用性を持つプログラミングスタイルのために、他のストリーム関数を文字列を生成するアクション。しかし、他の回答を通して考えてみると、なぜこれが一般的には解決できないのか分かります。私が提示した単純なケースでは、ストリームの順序付けがアクションの順序付けを暗示していると考えていたので、私が本当に望んでいたのはsequenceオペレータでした。実際、このような順序付けは必ずしも暗示されていません。これは、2つのストリームを入力として受け取るストリーム関数(例えば、ペアごとに2つのストリームを追加する)について考えると、私には明らかになります。両方の "着信"ストリームがIOを実行した場合、それらのIOアクションの順序は未定義です(もちろん、IOモナドで自分自身を順序付けして定義しない限り)。問題は解決しました、皆さんありがとう!

答えて

7

。 (。型シグネチャの変更に注意してください)主な機能は、その後、それらのアクションの2を取り、それらを実行し、結果を出力します。

a 
b 
"[\"a\",\"b\"]" 

タイプIO (String)はどのあなたができる任意の他のようなだけの機能/値であるので、これは動作しますリストに入れたり、渡したりします。do構文は "effectfulStreamFunc"には現れません。実際には署名の "IO"にもかかわらず純粋な関数です。メインにある人にsequenceを実行した場合にのみ、効果が実際に発生します。

+1

のように見えます。この場合

import System.IO.Unsafe (unsafeInterleaveIO) effectfulStreamFunc :: [String] -> IO [String] effectfulStreamFunc [] = return [] effectfulStreamFunc (x:xs) = unsafeInterleaveIO $ do putStrLn x rest <- effectfulStreamFunc xs return (reverse x : rest) main :: IO() main = do let fns = ["a", "b", "c", "d"] es <- effectfulStreamFunc fns print $ show $ take 2 es 

putAction = do putStrLn x return x in putAction:effectfulStreamFunc xs 少し良くなると思います。 –

+0

良い点。私は構文が実際にモナドを実行する問題に直交していると思います。 –

2

あなたの主な目標は本当に分かりませんが、putStrLnを使用すると、実行時に引数が評価されるため、リスト全体の評価が行われます。

import IO 

normalStreamFunc :: [String] -> [String] 
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs 

effectfulStreamFunc :: [String] -> IO [String] 
effectfulStreamFunc [] = return [] 
effectfulStreamFunc (x:xs) = do 
    rest <- effectfulStreamFunc xs 
    return (reverse(x):rest) 

main :: IO() 
main = do 
    let fns = ["a", "b", undefined,"c", "d"] 
    es <- effectfulStreamFunc fns 
    print $ show $ take 2 es 

それが例外を生じるputStrLnバージョンを使用しながら、これは、 "[\" \ "\ "Bの\"]" で検索結果考えます。

import IO 

normalStreamFunc :: [String] -> [String] 
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs 

effectfulStreamFunc :: [String] -> [IO (String)] 
effectfulStreamFunc [] = [] 
effectfulStreamFunc (x:xs) = 
    let rest = effectfulStreamFunc xs in 
     (putStrLn x >> return x) : rest 

main :: IO() 
main = do 
    let fns = ["a", "b", "c", "d"] 
    let appliedFns = effectfulStreamFunc fns 
    pieces <- sequence $ take 2 appliedFns 
    print $ show $ pieces 

よりもむしろeffectfulStreamFuncが実際にどのIOをやって、この1つは代わりに実行するためのIOアクションのリストを作成します。このコードについてはどのように

1

Tomhが述べたように、あなたはHaskellの参照透過性を壊しているので、これは実際には「安全に」できません。あなたは怠惰に副作用を遂行しようとしていますが、怠惰については、あなたが何の順序で評価されているのか、評価されているのか保証されていないということです。したがって、Haskellでは、指定された正確な順序で実行されます。 (つまり、returnの前に再帰呼び出しeffectfulStreamFuncの副作用がありました。これはリストされた順番だったためです)安全でない場合は、この遅延を遅延させることはできません。

のようなものを試すことができます。これは、Lasky IO(例:hGetContents)がHaskellに実装されている方法ですが、それ自体の問題があります。あなたは「安全でない」ものを使用したくないと言った。 effectfulStreamFunc(X::XS)= LETまだやる表記のようなあなたは、あなたにeffectfulStreamFuncの二句を書くことができた場合、出力はこの

"a 
[\"a\"b 
,\"b\"]" 
関連する問題