用适用范围实现Monoidal [英] Implementing Monoidal in terms of Applicative
问题描述
Typeclassopedia 提供以下练习:
根据单位和(**)实现纯和(*),反之亦然。
以下是 Monoidal
和 MyApplicative
:
class Functor f => Monoidal f其中
u :: f() - 使用`u`而不是`unit`
dotdot :: f a - > f b - > f(a,b) - 使用而不是`(**)`
class Functor f => MyApplicative f where
p :: a - > f a - 使用而不是`pure`
apply :: f(a - > b) - > f a - > fb - 使用而不是`(< **>)`
首先,让我展示 Maybe
-like数据类型:
数据选项a =
|没有派生显示
然后,我定义了 instance MyApplicative Option $ c $
instance MyApplicative Option其中
p =一些
适用无_ =无
apply _ None = None
apply(Some g)f = fmap gf
最后,我的按照 p
和应用
尝试实现 Monoid Option
MyApplicative
:
instance Monoidal Option其中
u = p ()
dotdot无_ = None
dotdot _ None = None
dotdot(Some x)(Some y)= Some id <*>一些(x,y)
是这样吗?使用 apply
执行 dotdot
似乎并不是
<$
= p()
dotdot无_ =无
dotdot _无=无
dotdot(Some x)( Some y)= apply(Some id)(Some(x,y))
对于如何正确实现 dotdot :: fa - > f b - > f(a,b)
与Applicative的(< *>)
- 在我的情况下,它是 apply
Applicative
Monoidal
。两个类型类都是等价的,您可以在两者之间进行转换,而不考虑特定的数据类型,如 Option
。 Applicative
的整齐替代表示法基于以下两个等同性
纯a = fmap(const a)单元
单元=纯()
ff * fa = fmap(\(f,a) - > f a)$ ff ** fa
fa ** fb =纯(,)* fa * fb
获得 Applicative
与 zipWith
的技巧相同 - 在接口中用类型或构造函数可以传入的东西来替换显式类型和构造函数,以恢复原始界面是。
unit :: f()
替换为 pure
,我们可以用()
和构造函数()::()
来恢复 unit
。
pure :: a - > fa
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)
c $ c>< /> Applicative
then通过提升函数应用程序($)::(a - > b) - >来获得好的
回到< a - > $ b $ p $(<>):: f(a - > ; b) - > f a - > fb
(< *>)= liftA2($)
liftA2
已经足够普遍了, liftA2
包含在 Control.Applicative code>
。 < $>
是中缀 fmap
。
liftA2 :: Applicative f => (a→b→c)→> f a - > f b - > f c
liftA 2 f a b = f< $> a * b
Typeclassopedia presents the following exercise:
Implement pure and (<*>) in terms of unit and (**), and vice versa.
Here's Monoidal
and 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 `(<**>)`
First, let me show the Maybe
-like data type:
data Option a = Some a
| None deriving Show
Then, I defined instance MyApplicative Option
:
instance MyApplicative Option where
p = Some
apply None _ = None
apply _ None = None
apply (Some g) f = fmap g f
Finally, my attempt at implementing Monoidal Option
in terms of p
and apply
of MyApplicative
:
instance Monoidal Option where
u = p ()
dotdot None _ = None
dotdot _ None = None
dotdot (Some x) (Some y) = Some id <*> Some (x, y)
Is this right? My implementation of dotdot
with apply
doesn't seem
instance Monoidal Option where
u = p ()
dotdot None _ = None
dotdot _ None = None
dotdot (Some x) (Some y) = apply (Some id) (Some (x, y))
In particular, I'm curious about how to properly implement dotdot :: f a -> f b -> f (a, b)
with Applicative's (<*>)
- in my case it's apply
.
Applicative
is a neat alternative presentation of Monoidal
. Both typeclasses are equivalent, and you can convert between the two without considering a specific data type like Option
. The "neat alternative presentation" for Applicative
is based on the following two equivalencies
pure a = fmap (const a) unit
unit = pure ()
ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb
The trick to get this "neat alternative presentation" for Applicative
is the same as the trick for zipWith
- replace explicit types and constructors in the interface with things that the type or constructor can be passed into to recover what the original interface was.
unit :: f ()
is replaced with pure
which we can substitute the type ()
and the constructor () :: ()
into to recover unit
.
pure :: a -> f a
pure () :: f ()
And similarly (though not as straightforward) for substituting the type (a,b)
and the constructor (,) :: a -> b -> (a,b)
into liftA2
to recover **
.
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 (,) :: f a -> f b -> f (a,b)
Applicative
then gets the nice <*>
operator by lifting function application ($) :: (a -> b) -> a -> b
into the functor.
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)
Getting from <*>
back to liftA2
is common enough that liftA2
is included in Control.Applicative
. The <$>
is infix fmap
.
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b
这篇关于用适用范围实现Monoidal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!