简体   繁体   English

functor,applicative和Monad之间的交互

[英]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. 由于您试图了解FunctorApplicativeMonad之间的关系,因此您可能想知道MonadApplicative实例不兼容。 (<*>) 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM