2011-01-22 53 views
30

我學習的單子有建設性的批評,這是我第一次(從小事單子除外)的工作之一。隨意批評其中的一切無情。我對「更習慣」和「更優雅」的迴應特別感興趣。尋求單子上執行

這個單子計數進行綁定數量。

data C a = C {value :: a, count :: Int} deriving (Show) 

instance Monad C where 
    (>>=) (C x c) f = C (value $ f x) (c + 1) 
    return x = C x 0 

add :: (Num a) => a -> a -> C a 
add x y = return $ x + y 

-- Simpler way to do this? foldM is obviously something different. 
mysum [x] = return x 
mysum (x:xs) = mysum xs >>= add x 

回答

88

風格上這是非常好的。在現實世界中,我希望這個符號,而不是你給了一個60%的機會:

C x c >>= f = C (value $ f x) (c + 1) 

但是,這是如此輕微它是幾乎不值得一提。

在更嚴重的注意,不是文體但語義:這不是一個單子。事實上,它違反了所有三項單子法。

(1) return x >>= f = f x 
(2) m >>= return = m 
(3) m >>= (f >=> g) = (m >>= f) >>= g 

(凡(>=>)被定義爲f >=> g = \x -> f x >>= g,如果(>>=)被認爲是一個「應用程序」操作,然後(>=>)是相應組成運營商。我喜歡,因爲它帶出了第三定律的陳述使用該運營商的第三定律這意味着:結合性)

隨着這些計算:

(1):

return 0 >>= return 
    = C 0 0 >>= return 
    = C (value $ return 0) 1 
    = C 0 1 
Not equal to return 0 = C 0 0 

(2):

C 0 0 >>= return 
    = C (value $ return 0) 1 
    = C 0 1 
Not equal to C 0 0 

(3)

C 0 0 >>= (return >=> return) 
    = C (value $ (return >=> return) 0) 1 
    = C (value $ return 0 >>= return) 1 
    = C (value $ C 0 1) 1 
    = C 0 1 

Is not equal to: 

(C 0 0 >>= return) >>= return 
    = C (value $ return 0) 1 >>= return 
    = C 0 1 >>= return 
    = C (value $ return 0) 2 
    = C 0 2 

這不只是在執行一個錯誤 - 沒有單子是 「計算綁定數量」。它必須違反法律(1)和(2)。但是,您違反法律(3)的事實是實施錯誤。

麻煩的是,(>>=)定義中的f可能會返回一個具有多個綁定的操作,而您忽略了該操作。您需要添加綁定數量從左邊和右邊的參數:

C x c >>= f = C y (c+c'+1) 
    where 
    C y c' = f x 

這將正確計算的綁定數量,並且將滿足第三定律,這是關聯性法律。它不會滿足其他兩個。但是,如果您從此定義中刪除+1,那麼做的會得到一個真正的monad,這相當於+ monoid上的Writer monad。這基本上將所有子計算的結果加在一起。你可以用這個來計算約會的數量,只是不綁定 - 綁定是非常特殊的數量。但是,例如:

tick :: C() 
tick = C() 1 

然後C將計算的發生在計算tick S上的號碼。

事實上,你可以替換Int與任何類型和(+)任何關聯運營商,並得到一個單子。一般來說,這就是monad的一個Writer。如果操作員不是聯想的,那麼這會違反第三條法則(你能明白爲什麼?)。

+0

這不是我所期待的,但肯定是我需要的。爲什麼如果操作者不是聯想的,第三定律被打破:因爲第三定律是結合的聯合性。如果綁定「做」了某些不相關的東西(我不能給出「確定」的確切定義),那麼它本身也不可能是關聯的。是對的嗎? – abesto 2011-01-22 01:12:32