2013-04-28 47 views
11

In a great series of posts Eric Lippert概述了.NET類型的所謂「Monad模式」,它類似monad並實現返回並綁定其中一些模式。Monadic .NET類型

由於單子類型的例子,他給出了:

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

我有兩個問題:

  1. 我得到Nullable<T>是一種像在Haskell Maybe並結合幾個Maybe行動表示一組可以在任何點失敗操作。我知道單子列表(IEnumerable<T>)代表非確定性。我甚至有點理解什麼Func作爲單子(Reader單子)。 什麼是Lazy<T>Task<T>的monadic sematnics?綁定他們意味着什麼?

  2. 有沒有人有任何更多的.NET類型的例子,有點像單子?

回答

1

的一元bind函數的類型爲:

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

所以在C#Task<T>你需要一個函數,它接受一個Task<A>提取的值,並把它傳遞給結合功能。如果任務錯誤或被取消,複合任務應傳播錯誤或取消。

這是非常簡單的使用異步:

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); 
} 

Lazy<T>你應該從另需懶惰計算的結果的函數來創建一個懶惰的價值:

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的價值所以你需要構造一個新的懶惰,它從創建的懶惰中解開值。

+0

Thnaks,正是我在找的! – Michael 2013-04-29 08:04:38

4

好吧,Haskell有默認懶惰,這樣就不會在Haskell很有指導意義,但我仍然可以說明如何實現Task S作爲單子。這裏是你將如何實現它們在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實例。

使用這個API,你可以很容易地定義自己的Task S,只是提供你想要當Task運行到餐桌的操作:

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小號新的延期任務準備運行:

test3 :: Task Int 
test3 = do 
    n1 <- test1 
    n2 <- test2 
    return (n1 + n2) 

運行fork test3將產生的Task並返回一個未來,你ç隨時調用以請求結果,必要時阻塞直到完成。

爲了證明它的工作原理,我會做兩個簡單的測試。首先,我將叉test3,而不要求其今後的公正,以確保它產生正確複合絲:

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

這正常工作:

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

現在,我們可以測試的時候,我們的需求會發生什麼結果:

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

...這也適用:

$ ./task 
Hello, 
world! 
3 
$ 
+0

這並不是我剛剛提出的問題,但是我們正在研究它 - 綁定任務會創建一個結果任務,它將同時執行所有任務,然後產生一些結果? – Michael 2013-04-28 20:39:21

+0

我在Eric Lippert的網站上使用'Task'的確切定義:「任務 - 表示一個正在異步計算的T,如果它還未被使用,將來可用。這正是我的'Task'類型所做的。您的問題的答案是肯定的:綁定任務創建一個新任務,在您運行時將同時執行所有任務。要求未來完成這項複合任務將等待所有子任務的完成。 – 2013-04-28 20:43:07

+0

Grate,謝謝,它回答我關於任務單點語義的問題。 – Michael 2013-04-28 20:47:44