[英]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.