2011-08-28 76 views
26

我剛剛重新發明了一些monad,但我不確定哪一個。它可以讓你對一個計算的步驟進行建模,這樣你就可以交錯計算大量的步驟,找出哪一步先完成。Haskell:我剛剛改造了哪個monad?

{-# LANGUAGE ExistentialQuantification #-} 
module Computation where 

-- model the steps of a computation 
data Computation a = forall b. Step b (b -> Computation a) | Done a 

instance Monad Computation where 
    (Step b g) >>= f = Step b $ (>>=f) . g 
    (Done b) >>= f = Step b f 
    return = Done 

runComputation :: Computation a -> a 
runComputation (Step b g) = runComputation (g b) 
runComputation (Done a) = a 

isDone :: Computation a -> Bool 
isDone (Done _) = True 
isDone _ = False 

-- an order for a set of computations 
data Schedule a = a :> Computation (Schedule a) | Last 

toList :: Schedule a -> [a] 
toList Last = [] 
toList (a :> c) = a : (toList . runComputation) c 

-- given a set of computations, find a schedule to generate all their results 
type Strategy a = [Computation a] -> Computation (Schedule a) 

-- schedule all the completed computations, and step the rest, 
-- passing the remaining to the given function 
scheduleOrStep :: (Queue (Computation a) -> Computation (Schedule a)) -> Strategy a 
scheduleOrStep s cs = scheduleOrStep' id cs 
    where scheduleOrStep' q ((Done a):cs) = Done $ a :> scheduleOrStep' q cs 
     scheduleOrStep' q ((Step b g):cs) = scheduleOrStep' (q . (g b:)) cs 
     scheduleOrStep' q [] = s q 

-- schedule all completed compuations, step all the rest once, and repeat 
-- (may never complete for infinite lists) 
-- checking each row of 
-- [ [ c0s0, c1s0, c2s0, ... ] 
-- , [ c0s1, c1s1, c2s1, ... ] 
-- , [ c0s2, c1s2, c2s2, ... ] 
-- ... 
-- ] 
-- (where cNsM is computation N stepped M times) 
fair :: Strategy a 
fair [] = Done Last 
fair cs = scheduleOrStep (fair . ($[])) cs 

-- schedule more steps for earlier computations rather than later computations 
-- (works on infinite lists) 
-- checking the sw-ne diagonals of 
-- [ [ c0s0, c1s0, c2s0, ... ] 
-- , [ c0s1, c1s1, c2s1, ... ] 
-- , [ c0s2, c1s2, c2s2, ... ] 
-- ... 
-- ] 
-- (where cNsM is computation N stepped M times) 
diag :: Enqueue (Computation a)-> Strategy a 
diag _ [] = Done Last 
diag enq cs = diag' cs id 
    where diag' (c:cs) q = scheduleOrStep (diag' cs) (enq c q $ []) 
     diag' [] q = fair (q []) 

-- diagonal downwards : 
-- [ c0s0, 
-- c1s0, c0s1, 
-- c2s0, c1s1, c0s2, 
-- ... 
-- cNs0, c{N-1}s1, ..., c1s{N-1}, c0sN, 
-- ... 
-- ] 
diagd :: Strategy a 
diagd = diag prepend 

-- diagonal upwards : 
-- [ c0s0, 
-- c0s1, c1s0, 
-- c0s2, c1s1, c2s0, 
-- ... 
-- c0sN, c1s{N-1}, ..., c{s1N-1}, cNs0, 
-- ... 
-- ] 
diagu :: Strategy a 
diagu = diag append 

-- a queue type 
type Queue a = [a] -> [a] 
type Enqueue a = a -> Queue a -> Queue a 

append :: Enqueue a 
append x q = q . (x:) 

prepend :: Enqueue a 
prepend x q = (x:) . q 

我覺得這可能是某種線程monad?

+18

哈斯克爾是我所知道的唯一一種語言,你無法分辨出你剛剛重新創建了哪個輪子... –

+0

我即將關閉_too localized_,但人們是否真的花時間知道他們正在重塑Haskell中的東西但不是他們正在重塑的問題,使這個問題變得合法(假設很多人最終會重塑這個確切的東西,不管它是什麼)? – Mat

+11

@Mat:是的,其實。至少在某些方面。在Haskell中,人們偶爾會做出一些不太有趣的事情,只要給出足夠的泛型代碼,如果它的類型檢查幾乎肯定會做一些有用的事情,即使你不確定是什麼。這有點類似,因爲如果你發明了某些東西來解決某個具體問題,並且它很容易理解,那麼很可能已經完成了。當我第一次學習Haskell時,至少有一次我推廣了一個特定的解決方案,只是意識到我已經改造了標準庫中一個不起眼的角落。 –

回答

2

我沒花太多時間來理解你的代碼,但它聽起來像monad-coroutine包中的協程monad,它可能有點更一般。

+0

我簡要地瞥了一眼 - 它看起來像是基於通信的協同程序,而它們是非交流的。 – rampion

2

這看起來與Don Stewart先前使用的流融合的定義類似,也與迭代有些相關(儘管沒有使用枚舉器將數據推入迭代器的概念),但不如流合成我猜。

5

我不明白爲什麼不

data Computation a = Step (Computation a) | Done a 

instance Monad Computation where 
    (Step g) >>= f = Step $ g >>= f 
    (Done b) >>= f = Step (f b) 
    return = Done 

我不知道這個單子是什麼,但它絕對簡單,似乎是在許多方面等價的。

+0

好點。我喜歡。 – rampion

+1

這是簡單的恢復monad。 Rampion的原始monad具有類似'unfoldr'函數的線程狀態,但我無法真正瞭解這個狀態對於給出的例子是否必要。在他的一篇論文中,威廉·哈里森評論說恢復單子基本上是「惰性的」,但沒有增加狀態(惰性不是他的原始單詞,但目前我找不到這個單詞)。 –

+1

@stephen tetley:然而,我已經給出了狀態的存在性量化,沒有什麼*可以用它來完成,所以實際上,它只是延遲了計算。無論如何懶惰的評估是什麼,所以它相當於恢復monad。所以這就是答案。 – rampion