簡體   English   中英

functor,applicative和Monad之間的交互

[英]Interaction between functor, applicative and Monad

我對Haskell完全陌生,我想更好地了解functor,applicative和monad如何一起工作。 在下面的示例中:

import Control.Monad
import Control.Applicative

data FooBar a = Foo a | Bar a deriving (Show)

myf :: FooBar Int -> FooBar Int
myf (Bar a) = Foo (a * 10)
myf (Foo a) = Bar (a * 10)

instance Functor FooBar where
    fmap func (Foo val) = Bar (func val)
    fmap func (Bar val) = Foo (func val)

instance Applicative FooBar where
    pure = Foo
    (Foo f) <*> (Foo x) = Foo (f x)
    (Foo f) <*> (Bar x) = Foo (f x)
    (Bar f) <*> (Foo x) = Bar (f x)
    (Bar f) <*> (Bar x) = Bar (f x)

instance Monad FooBar where
    return = Foo
    (Foo x) >>= f = f x
    (Bar x) >>= f = f x

main = putStrLn $ show $ Foo (+3) <*> Foo 5 >>= myf

我想要達到的目的是通過monad的綁定從Functor / Applicative中“傳遞”值,但在main行中出現錯誤:

ghc: No instance for (Num (FooBar Int)) arising from a use of `+'
Possible fix: add an instance declaration for (Num (FooBar Int))
In the first argument of `Foo', namely `(+ 3)'
In the first argument of `(<*>)', namely `Foo (+ 3)'
In the first argument of `(>>=)', namely `Foo (+ 3) <*> Foo 5'

如果我用Functor替換Applicative,類似的事情也會發生:

main = putStrLn $ show $ (+3) <$> Foo 5 >>= myf

實際上可能是我要嘗試做的事情,還是我的定義有誤?

編輯這是一個更干凈的解決方案:

import Control.Monad
import Control.Applicative

data FooBar a = Foo a | Bar a deriving (Show)

myf :: Int -> FooBar Int
myf (a) = return (a * 10)

instance Functor FooBar where
    fmap func (Foo val) = Foo (func val)
    fmap func (Bar val) = Bar (func val)

instance Applicative FooBar where
    pure = Foo
    (Foo f) <*> something = fmap f something
    (Bar f) <*> something = fmap f something

instance Monad FooBar where
    return = Foo
    (Foo x) >>= f = f x
    (Bar x) >>= f = f x

main = putStrLn $ show $ (+) <$> Bar(19) <*> (Foo 3) >>= myf

問題在這里:

myf :: FooBar Int -> FooBar Int

以上使用時會造成麻煩

something >>= myf

因為它要求something具有FooBar (FooBar Int)類型。 反過來,這會使數字常量的類型為FooBar Int而不是Int ,並且(+)對類型為FooBar Int “數字”進行操作。 這會觸發類型錯誤。

也許您只是想使用

myf something

代替。 在您的特定情況下,

main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5

由於您試圖了解FunctorApplicativeMonad之間的關系,因此您可能想知道MonadApplicative實例不兼容。 (<*>)行為必須與Control.Monad.ap相同:

ap :: (Monad m) => m (a -> b) -> m a -> m b
ap mf mx = mf >>= (\f -> mx >>= (\x -> return (f x)))

但是你有:

Bar id <*>  Bar 0 = Bar 0
Bar id `ap` Bar 0 = Foo 0

實際上,造成這種情況的原因還導致違反了monad法:

m >>= return = m

但是你有

Bar 0 >>= return = Foo 0

同樣的事情也違反了適用法律。

您不能簡單地丟棄有關值是按Foo還是Bar構造的信息。 由於return = Foo ,因此您需要確保Foo表現為“純正”-即與Foo組合(在(<*>)(>>=) )不會改變另一個參數的結構。 解決此問題的一種可能方法是始終讓Bar “污染”計算:

-- Since we have a Foo, we need to preserve whatever f does:
Foo x >>= f = f x
Bar x >>= f = case f x of   
                  -- If f x returned a Foo, we need to preserve Bar from the left arg:
                  Foo y -> Bar y
                  -- We can do whatever we like with this clause:
                  Bar y -> Bar y

然后使用ap找出您的Applicative實例應該是什么。 有趣的是,這與Writer Any同構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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