Monadic Computationの順序付け制約を緩和します
-
29-10-2019 - |
質問
これが思考のための食べ物です。
Monadic Codeを書くと、Monadは行われた操作に注文を課します。たとえば、IO Monadに書いた場合:
do a <- doSomething
b <- doSomethingElse
return (a + b)
知っている doSomething
前に実行されます doSomethingElse
.
ここで、Cのような言語の同等のコードを考えてみましょう:
return (doSomething() + doSomethingElse());
Cのセマンティクスは、実際にこれらの2つの関数呼び出しが評価される順序を指定しないため、コンパイラは自由に動き回ることができます。
私の質問はこれです: HaskellにMonadic Codeを書くにはどうすればよいですか。 理想的には、コンパイラのオプティマイザーがコードを見て、物事を動かし始めたときに、いくつかのメリットを享受します。
仕事を終わらせないが、正しい「精神」にあるいくつかの可能なテクニックを以下に示します。
- にコードを書きます 機能スタイル, 、つまり、書く
plus doSomething doSomethingElse
とさせてくださいplus
モナディックコールをスケジュールします。欠点:あなたはモナディックの行動の結果について共有を失うこと、そしてplus
物事がいつ評価されるかについてまだ決定を下します。 - 使用する 怠zyio, 、 あれは、
unsafeInterleaveIO
, 、評価の怠zyな要求へのスケジューリングを扱います。しかし、怠zyは未定義の順序で厳格とは異なります。特に、私は私のすべてのモナディの行動を実行したいと思っています。 - 怠zyは、すぐにすべての議論を一致させた。特に、
seq
順序付けの制約は課されません。
この意味で、私はモナディックの注文よりも柔軟なものを望んでいますが、フルアウトの怠inessよりも柔軟性が低くなります。
解決
モナドコードを過度に順番化するこの問題は、 「通勤モナドの問題」.
通勤モナドは、アクションの順序が違いを生じさせないモナドです(通勤)。つまり、次のコードが次の場合です。
do a <- f x
b <- g y
m a b
と同じです:
do b <- g y
a <- f x
m a b
通勤する多くのモナドがあります(例: Maybe
, Random
)。モナドが通勤している場合、その中でキャプチャされた操作は、たとえば並行して計算できます。彼らはとても便利です!
しかし、私たち 通勤するモナドに適した構文はありません, 、 けれど 多くの人が尋ねました そのようなことのために - それはまだです オープンな研究問題.
余談ですが、アプリケーションファンクターは私たちに計算を並べ替える自由を与えてくれますが、あなたはの概念をあきらめる必要があります bind
(例: liftM2
見せる)。
他のヒント
これは深い汚いハックですが、私にはトリックをするべきだと思われます。
{-# OPTIONS_GHC -fglasgow-exts #-}
{-# LANGUAGE MagicHash #-}
module Unorder where
import GHC.Types
unorder :: IO a -> IO b -> IO (a, b)
unorder (IO f) (IO g) = IO $ \rw# ->
let (# _, x #) = f rw#
(# _, y #) = g rw#
in (# rw# , (x,y) #)
これはコンパイラの手に非決定的なものを置くため、流れの問題(つまり例外)を制御することに関して、「正しく」(つまり非決定的に)振る舞うはずです。
一方、私たちは同じトリックを引っ張ることはできません、ほとんどの標準的なモナドは State
と Either a
私たちは、で利用できる距離で不気味な行動に本当に頼っているので RealWorld
トークン。適切な動作を得るには、オプティマイザーが利用できる注釈が必要であり、2つの非同等の代替品からの非決定的な選択で問題があることを示しました。
Cのセマンティクスは、実際にこれらの2つの関数呼び出しが評価される順序を指定しないため、コンパイラは自由に動き回ることができます。
しかし、もしも doSomething()
の動作を変える副作用を引き起こします doSomethingElse()
?コンパイラに注文を台無しにしたいですか? (ヒント:いいえ)あなたがモナドにいるという事実は、そのようなことがそうかもしれないことを示唆しています。 「結果で共有を失う」というメモもこれを示唆しています。
ただし、モナディックは常にシーケンスを意味するとは限らないことに注意してください。それはあなたが説明するものではありませんが、あなたはに興味があるかもしれません パーモナド, 、これにより、アクションを並行して実行できます。
コンパイラが魔法のように最適化できるように、注文を未定義のままにしておくことに興味があります。代わりに、代わりに、パーモナドのようなものを使用して、依存関係を示し(他の人の前に必然的に起こらなければならないものもあります)、残りを並行して実行する必要があります。
サイドノート:Haskellを混乱させないでください return
Cのようなものになること return