簡體   English   中英

Haskell:實施簡單的Monad案例研究時遇到問題

[英]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實例塊)調用add5add7 ,它的工作原理是:

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->aa->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的超類,您將必須定義其他實例。 在此示例中,它們無關緊要,因此我將省略它們。

現在,您可以定義add5add7

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 -> aa -> (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 MonadReader 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM