[英]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
由於您試圖了解Functor
, Applicative
和Monad
之間的關系,因此您可能想知道Monad
和Applicative
實例不兼容。 (<*>)
行為必須與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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.