functor、applicative 和 Monad 之间的交互
Interaction between functor, applicative and Monad
我是 Haskell 的新手,我正试图更好地理解仿函数、应用函数和 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
我想要实现的是 "piping" 通过 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
类型的 "numbers" 进行操作。这会触发
输入错误。
也许你只是想使用
myf something
相反。在您的特定情况下,
main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
由于您试图了解 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
的行为与 "purely" 一致——即与其结合(在 (<*>)
或 (>>=)
) 不会改变另一个参数的结构。解决此问题的一种可能方法是始终进行 Bar
"taint" 计算:
-- 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
我是 Haskell 的新手,我正试图更好地理解仿函数、应用函数和 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
我想要实现的是 "piping" 通过 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
类型的 "numbers" 进行操作。这会触发
输入错误。
也许你只是想使用
myf something
相反。在您的特定情况下,
main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
由于您试图了解 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
的行为与 "purely" 一致——即与其结合(在 (<*>)
或 (>>=)
) 不会改变另一个参数的结构。解决此问题的一种可能方法是始终进行 Bar
"taint" 计算:
-- 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