2013-04-28 9 views
11

In a great series of posts Eric Lippertは、モナドのように動作し、一部のオブジェクトに対してリターンとバインドを実装する.NETタイプの、いわゆる「モナドパターン」の概要を説明します。Monadic .NETタイプ

モナド型の例として、彼は与える:

  • Nullable<T>
  • Func<T>
  • Lazy<T>
  • Task<T>
  • IEnumerable<T>

私はNullable<T>は一種のHaskellではMaybeのようなもので、いくつかのMaybeアクションを結合すると、任意の時点で失敗する可能性が一連の操作を表していることを得る

  1. :私は2つの質問があります。私はリストモナド(IEnumerable<T>)が非決定論を表していることを知っています。私はちょっと、Funcがモナド(Readerモナド)と同じことを理解しています。 Lazy<T>Task<T>というモナディのsematnicsは何ですか?それらを結びつけるのはどういう意味ですか?

  2. 誰もが.NETの型のこれ以上の例を持っていますか?

答えて

1

モナドbind関数は型を持ちます。タスクのエラーまたはキャンセルがキャンセルされた場合、複合タスクはエラーまたはキャンセルを伝播する必要があります。

これは非同期を使用して非常に簡単です:

Lazy<T>ため
public static async Task<B> SelectMany<A, B>(this Task<A> task, Func<A, Task<B>> bindFunc) 
{ 
    var res = await task; 
    return await bindFunc(res); 
} 

あなたが別の怠惰な計算の結果を取る関数から怠惰な値を作成する必要があります。

public static Lazy<B> SelectMany<A, B>(this Lazy<A> lazy, Func<A, Lazy<B>> bindFunc) 
{ 
    return new Lazy<B>(() => bindFunc(lazy.Value).Value); 
} 

私は

と思います
return bindFunc(lazy.Value); 

は熱心に012の値を評価するため無効ですですので、作成した遅延から値をアンラップする新しいlazyを構築する必要があります。

+0

Thnaks、まさに私が探していたもの! – Michael

4

それはHaskellで非常に有益ではないだろうが、私はまだモナドとしてTask Sを実装する方法を示すことができるので、まあ、Haskellは、デフォルトで怠惰を持っています。ここでは、Haskellでそれらを実装するだろう方法は次のとおりです。

import Control.Concurrent.Async (async, wait) 

newtype Task a = Task { fork :: IO (IO a) } 

newTask :: IO a -> Task a 
newTask io = Task $ do 
    w <- async io 
    return (wait w) 

instance Monad Task where 
    return a = Task $ return (return a) 
    m >>= f = newTask $ do 
     aFut <- fork m 
     a <- aFut 
     bFut <- fork (f a) 
     bFut 

それは便宜上asyncライブラリに基づいていますが、それはする必要はありません。 async関数が行うことは、アクションを評価して未来を返すスレッドをフォークすることです。私はそれの周りに小さなラッパーを定義して、Monadインスタンスを定義することができます。

import Control.Concurrent (threadDelay) 

test1 :: Task Int 
test1 = newTask $ do 
    threadDelay 1000000 -- Wait 1 second 
    putStrLn "Hello," 
    return 1 

test2 :: Task Int 
test2 = newTask $ do 
    threadDelay 1000000 
    putStrLn " world!" 
    return 2 

次に、あなたが作成するdo表記法を使用してTaskを組み合わせることができます。このAPIを使用して

、あなたはちょうどあなたがTaskが実行されたときにフォークしたいアクションを供給することにより、簡単に独自のTask Sを定義することができます実行する準備ができて新しい繰延タスク:

fork test3Taskを起動し、あなたのC未来を返します実行
test3 :: Task Int 
test3 = do 
    n1 <- test1 
    n2 <- test2 
    return (n1 + n2) 

結果を要求するためにいつでも呼び出され、完了するまで必要であればブロックされます。

動作することを示すために、2つの簡単なテストを行います。これが正しく動作

main = do 
    fork test3 
    getLine -- wait without demanding the future 

:まず、私はちょうどそれが正しく複合スレッドを生成を確認するために、将来を要求せずにtest3をフォークます

$ ./task 
Hello, 
world! 
<Enter> 
$ 

今、私たちは私たちが要求するときに何が起こるかをテストすることができます結果:

main = do 
    fut <- fork test3 
    n <- fut -- block until 'test3' is done 
    print n 

...も動作します:

Task<T>のためにC#であなたが Task<A>抽出値を取り、結合機能に渡し機能を必要とするので、

Moand m => m a -> (a -> m b) -> m b 

:10

+0

これは私が求めていたものではありませんでしたが、私たちはそれに取り組んでいる間に、すべてのタスクを同時に実行して結果を生み出す結果のタスクを作成します。 – Michael

+0

私はEric Lippertのサイト上で 'Task'の正確な定義を使用しています:" Task - 非同期的に計算されているTを表します。それはまさに私の 'タスク'タイプがしているものです。あなたの質問に対する答えは「はい」です。バインディングタスクは、実行時にすべてのタスクを同時に実行する新しいタスクを作成します。その複合タスクから未来への要求は、すべてのサブタスクが完了するのを待つでしょう。 –

+0

グレート、ありがとう、それは仕事のモナドの意味論に関する私の質問に答える。 – Michael

関連する問題