2011-01-22 9 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

これは非常に素晴らしいです。現実の世界では、私の代わりに、あなたが与えた1のこの表記法の60%の確率で期待される:

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

をしかし、それはそれは言及ほとんど価値があるので、軽微であります。

より深刻なことに、文体的ではなく意味論的なものです:これはモナドではありません。実際、それはモナド法の3つすべてに違反しています。 (>=>)f >=> g = \x -> f x >>= gとして定義されている。(>>=)は、その後(>=>)は、対応する組成演算子であり、「アプリケーション」演算子とみなされた場合。私は、この演算子を使用して第3法則を述べるのが好き、それは第3法則のを引き出すため

(1) return x >>= f = f x 
(2) m >>= return = m 
(3) m >>= (f >=> g) = (m >>= f) >>= 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 

これは正しくバインドの数をカウントします、と結合法である第3法則を、満足させます。それは他の2つを満たしません。しかし、+1をこの定義から削除すると、はモノイド以上のWriterモナドに相当する実際のモナドを取得します。これは基本的にすべてのサブコンピューティングの結果をまとめます。あなたは何かの数を数えるためにこれを使うことができます、ちょうどバインドしません - バインドはカウントするにはあまりにも特別です。しかし、例えば:次に

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

Cは、計算で発生したtick秒数をカウントします。

実際には、あなたはどの連想演算子を持つ任意の型を持つInt(+)を交換し、モナドを得ることができます。これは一般的にはWriterのモナドです。演算子が連想でない場合、これは3番目の法則に失敗します(理由は分かりますか?)。

+0

これは私が期待していたものではありませんが、私が必要としていたものです。なぜ3番目の法則が壊れているのか?なぜなら、演算子が結合的でないならば、3番目の法則は結合の結合性であるからです。バインドが(私が "does"の正確な定義を与えることができない)アソシエイティブでないものを "行う"場合、それ自体はアソシエイティブでもありません。そうですか? – abesto

+10

他にも:きれいな詳細な説明。ありがとうございました。 – abesto

+1

時間をかけて説明してくれてありがとう! – Daniel

関連する問題