仿函数,应用程序和Monad之间的交互

Ran*_*ize 1 monads haskell functor applicative

我是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
Run Code Online (Sandbox Code Playgroud)

我想要实现的是通过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'
Run Code Online (Sandbox Code Playgroud)

如果我用这样的Functor替换Applicative会发生类似的事情:

main = putStrLn $ show $ (+3) <$> Foo 5 >>= myf
Run Code Online (Sandbox Code Playgroud)

实际上我可能会做什么,或者我的定义中有错误?

编辑 这是一个更清洁的解决方案:

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
Run Code Online (Sandbox Code Playgroud)

chi*_*chi 5

问题出在这里:

myf :: FooBar Int -> FooBar Int
Run Code Online (Sandbox Code Playgroud)

以上使用时会造成麻烦

something >>= myf
Run Code Online (Sandbox Code Playgroud)

因为它需要something有类型FooBar (FooBar Int).这反过来使数字常量是类型FooBar Int而不是Int,并对(+)类型的"数字"进行操作FooBar Int.这会触发类型错误.

也许你只是想用

myf something
Run Code Online (Sandbox Code Playgroud)

代替.在你的特定情况下,

main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
Run Code Online (Sandbox Code Playgroud)