[英]Interaction between functor, applicative and Monad
I am totally new to Haskell and I am trying to understand better how functor, applicative and monad work together. 我对Haskell完全陌生,我想更好地了解functor,applicative和monad如何一起工作。 Below in my example:
在下面的示例中:
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
What I am trying to achieve is "piping" the value from a Functor/Applicative via monad's bind but I get an error in the main
line: 我想要达到的目的是通过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'
Something similar happens if I replace the Applicative with the Functor like this: 如果我用Functor替换Applicative,类似的事情也会发生:
main = putStrLn $ show $ (+3) <$> Foo 5 >>= myf
Is actually possible what I am trying to do or there is a mistake in my definitions? 实际上可能是我要尝试做的事情,还是我的定义有误?
EDIT This is a cleaner solution: 编辑这是一个更干净的解决方案:
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
The problem is here: 问题在这里:
myf :: FooBar Int -> FooBar Int
The above causes trouble when you use 以上使用时会造成麻烦
something >>= myf
because it requires something
to have type FooBar (FooBar Int)
. 因为它要求
something
具有FooBar (FooBar Int)
类型。 This in turn makes numeric constants to be of type FooBar Int
and not Int
, and (+)
operate on "numbers" of type FooBar Int
. 反过来,这会使数字常量的类型为
FooBar Int
而不是Int
,并且(+)
对类型为FooBar Int
“数字”进行操作。 This triggers the type error. 这会触发类型错误。
Maybe you simply want to use 也许您只是想使用
myf something
instead. 代替。 In your particular case,
在您的特定情况下,
main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
Since you are trying to understand the relationship between Functor
, Applicative
, and Monad
, you might like to know that your Monad
and Applicative
instances are incompatible. 由于您试图了解
Functor
, Applicative
和Monad
之间的关系,因此您可能想知道Monad
和Applicative
实例不兼容。 (<*>)
must behave the same way as Control.Monad.ap
: (<*>)
行为必须与Control.Monad.ap
相同:
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap mf mx = mf >>= (\f -> mx >>= (\x -> return (f x)))
but you have: 但是你有:
Bar id <*> Bar 0 = Bar 0
Bar id `ap` Bar 0 = Foo 0
In fact, what is causing this is also causing a monad law to be violated: 实际上,造成这种情况的原因还导致违反了monad法:
m >>= return = m
but you have 但是你有
Bar 0 >>= return = Foo 0
There is also an applicative law violated caused by the same thing. 同样的事情也违反了适用法律。
You cannot simply throw away the information of whether the value was constructed with Foo
or Bar
the way you are. 您不能简单地丢弃有关值是按
Foo
还是Bar
构造的信息。 Since return = Foo
, you need to make sure that Foo
behaves "purely" -- ie combining with it (in (<*>)
or (>>=)
) does not change the structure of the other argument. 由于
return = Foo
,因此您需要确保Foo
表现为“纯正”-即与Foo
组合(在(<*>)
或(>>=)
)不会改变另一个参数的结构。 One possible way out of this is to always have Bar
"taint" the computation: 解决此问题的一种可能方法是始终让
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
Then use ap
to find out what your Applicative
instance should be. 然后使用
ap
找出您的Applicative
实例应该是什么。 Interesting to note, this is now isomorphic to Writer
Any
. 有趣的是,这与
Writer
Any
同构。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.