用适用范围实现Monoidal [英] Implementing Monoidal in terms of Applicative

查看:106
本文介绍了用适用范围实现Monoidal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  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)

Applicative then通过提升函数应用程序($)::(a - > b) - >来获得好的< a - > $ b $ p $(<>):: f(a - > ; b) - > f a - > fb
(< *>)= liftA2($)

c $ c>< /> 回到 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆