繁体   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