Typeclassopedia提供以下练习:
以单位和(**)表示纯和(< *>),反之亦然。
此处Monoidal
和MyApplicative
:
class Functor f => Monoidal f where
u :: f () -- using `u` rather than `unit`
dotdot :: f a -> f b -> f (a,b) -- using instead of `(**)`
class Functor f => MyApplicative f where
p :: a -> f a -- using instead of `pure`
apply :: f (a -> b) -> f a -> f b -- using instead of `(<**>)`
首先,让我展示Maybe
- 类似的数据类型:
data Option a = Some a
| None deriving Show
然后,我定义了instance MyApplicative Option
:
instance MyApplicative Option where
p = Some
apply None _ = None
apply _ None = None
apply (Some g) f = fmap g f
最后,我尝试根据Monoidal Option
的{{1}}和p
实施apply
:
MyApplicative
这是对的吗?我instance Monoidal Option where
u = p ()
dotdot None _ = None
dotdot _ None = None
dotdot (Some x) (Some y) = Some id <*> Some (x, y)
与dotdot
的实施似乎不是
apply
特别是,我很好奇如何使用Applicative instance Monoidal Option where
u = p ()
dotdot None _ = None
dotdot _ None = None
dotdot (Some x) (Some y) = apply (Some id) (Some (x, y))
正确实施dotdot :: f a -> f b -> f (a, b)
- 在我的情况下(<*>)
。
答案 0 :(得分:10)
Applicative
是Monoidal
的简洁替代演示文稿。两个类型类都是等价的,您可以在两者之间进行转换,而不考虑像Option
这样的特定数据类型。 &#34;整洁的替代演示&#34; for Applicative
基于以下两个等价
pure a = fmap (const a) unit
unit = pure ()
ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb
获得这个&#34;整洁的替代演示文稿&#34; for Applicative
与zipWith
的技巧相同 - 将接口中的显式类型和构造函数替换为可以传递类型或构造函数的东西,以恢复原始接口的内容。
unit :: f ()
替换为pure
,我们可以将()
类型和构造函数() :: ()
替换为unit
。
pure :: a -> f a
pure () :: f ()
同样(尽管不是那么简单)将类型(a,b)
和构造函数(,) :: a -> b -> (a,b)
替换为liftA2
以恢复**
。
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 (,) :: f a -> f b -> f (a,b)
Applicative
然后通过将函数应用程序<*>
提升到仿函数中来获取漂亮的($) :: (a -> b) -> a -> b
运算符。
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)
从<*>
回到liftA2
已经足够liftA2
is included in Control.Applicative
了。 <$>
是中缀fmap
。
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b