[英]Haskell: Problems implementing a simple Monad case study
我通過實現一個簡單的示例開始研究Monad,但是我的Monad實例未編譯。
我想做類似的事情:
add5 7 >>= add7
此代碼必須返回19 [(5 + 7)>> =(12 + 7)]
我實現的代碼是:
newtype MyType a = MyType ( a -> a)
instance Monad MyType where
MyType comm >>= comm2 = MyType (\inp -> let
value = comm inp
MyType comm2' = comm2
in comm2' value)
return x = MyType (\input -> input)
add5 :: MyType Integer
add5 = MyType (\inp -> inp + 5)
add7 :: MyType Integer
add7 = MyType (\inp -> inp + 7)
當我不使用綁定運算符(通過評論Monad實例塊)調用add5
和add7
,它的工作原理是:
main = do
let MyType x = add5
let MyType y = add7
putStrLn $ show $ x $ y 7
輸出錯誤為:
new1.hs:5:94:
Couldn't match expected type `a' with actual type `b'
`a' is a rigid type variable bound by
the type signature for
>>= :: MyType a -> (a -> MyType b) -> MyType b
at new1.hs:4:9
`b' is a rigid type variable bound by
the type signature for
>>= :: MyType a -> (a -> MyType b) -> MyType b
at new1.hs:4:9
In the first argument of `comm', namely `inp'
In the expression: comm inp
In an equation for `value': value = comm inp
new1.hs:6:97:
Couldn't match expected type `MyType t0'
with actual type `a -> MyType b'
In the expression: comm2
In a pattern binding: MyType comm2' = comm2
In the expression:
let
value = comm inp
MyType comm2' = comm2
in comm2' value
它不能是Monad
,因為它甚至都不是Functor
,因為您在反變量位置具有類型變量。
這意味着您不能實現:
fmap :: (a->b)->MyType a->MyType b
您可以使用f :: a->b
改變結果的類型a->a
至a->b
,但你不能參數的類型更改為函數來得到b->b
,這是需要構造MyType b
。
我不確定您實際要做什么。 如果您只是想獲取代碼示例
add5 7 >>= add7
通過以“顯而易見的”方式將數字相加來工作並產生19
的結果,那么這很簡單, 任何 monad都可以。 因此,我們可以選擇最簡單的monad,即“ identity” monad:
newtype Id a = Id { runId :: a }
instance Monad Id where
return x = Id x
Id x >>= f = f x
請注意,此代碼將在GHC 7.8中產生警告,因為將來, Applicative
將成為monad的超類,您將必須定義其他實例。 在此示例中,它們無關緊要,因此我將省略它們。
現在,您可以定義add5
和add7
:
add5 :: Id Int
add5 n = return (n + 5)
add7 :: Id Int
add7 n = return (n + 7)
如果省略類型簽名並詢問GHCi,您將看到兩個定義實際上都具有更通用的類型(Num a, Monad m) => a -> ma
。 我的意思是說您的示例適用於任何 monad。
您可以嘗試使用GHCi進行所有操作:
GHCi> :t add5 7 >>= add7
add5 7 >>= add7 :: Id Int
GHCi> runId (add5 7 >>= add7)
19
您走錯了路。 MyType
不能是monad。 MyType
唯一可能的monad實例實現相當瑣碎,並且無法使add5 7 >>= add7
等於19
。
>>=
必須具有類型
MyType a -> (a -> MyType b) -> MyType b -- remove newType
(a -> a) -> (a -> (b -> b)) -> (b -> b)
類型檢查的唯一功能是
(MyType _) >>= _ = MyType (\input -> input)
看起來非常類似於您的return
實現。 我們通常在haskell中寫id
而不是(\\input -> input)
input- (\\input -> input)
。
為什么我聲稱這是唯一的功能? 再次檢查簡化的類型簽名(a -> a) -> (a -> (b -> b)) -> (b -> b)
:如果不使用a
作為輸入,則參數>>=
, a -> a
和a -> (b -> b)
不能求值。
這不滿足單子法則: x >>= return = x
MyType (\x -> x + 1) >>= return
=MyType id
/=MyType (\x -> x + 1)
您不需要Monad就可以做到。 相反,您可以使用Arrows :
Prelude> :m + Control.Arrow
Prelude Control.Arrow> let add5 = (+5)
Prelude Control.Arrow> let add7 = (+7)
Prelude Control.Arrow> let add12 = add5 >>> add7
Prelude Control.Arrow> add12 7
19
對於函數實例, (>>>)
函數只是組合運算符(即(.)
),其參數已翻轉。 因此,您可以像這樣簡單地做:
Prelude> let add5 = (+5)
Prelude> let add7 = (+7)
Prelude> let add12 = add7 . add5
Prelude> add12 7
19
已經有一個monad函數實例。 它被稱為Reader Monad : Reader Monad的目的是什么? 。
instance Monad ((->) r) where
return x = \_ -> x
f >>= g = \x -> g (f x) x
它允許您執行以下操作:
a2-minus-b2 = \a -> do
a-minus-b <- (\b -> a - b)
a-plus-b <- (\b -> a + b)
return (a-minus-b * a-plus-b)
當然,最好將其寫為:
a2-minus-b2 = \a b -> (a - b) * (a + b)
但是,我只想向您展示讀者monad可以用來做什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.