我是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
我想要实现的是&#34;管道&#34; Funator / Applicative通过monad绑定的值,但我在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
答案 0 :(得分:5)
问题在于:
myf :: FooBar Int -> FooBar Int
使用
时会出现上述问题something >>= myf
因为something
需要FooBar (FooBar Int)
类型。
这反过来使数字常量为FooBar Int
而不是Int
,
并(+)
对&#34;数字进行操作&#34;类型为FooBar Int
。这触发了
类型错误。
也许您只想使用
myf something
代替。在您的特定情况下,
main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
答案 1 :(得分:4)
由于您试图了解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
“纯粹”行事 - 即与(<*>)
或(>>=)
合并不改变其他参数的结构。一种可能的方法是始终让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