Monoidal Functor是适用的,但是Applicative定义中的Monoid类型类是什么?

时间:2018-06-05 14:45:32

标签: haskell applicative

Applicative是Monoidal Functor:

mappend :: f         -> f   -> f
$       ::  (a -> b) ->   a ->   b
<*>     :: f(a -> b) -> f a -> f b

但是我没有在应用类型类的定义中看到有关Monoid的任何参考,你能告诉我为什么吗?

定义:

class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
  GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c
  (*>) :: f a -> f b -> f b
  (<*) :: f a -> f b -> f a
  {-# MINIMAL pure, ((<*>) | liftA2) #-}

此定义中没有提到结构Monoid,但是当你这样做时

> ("ab",(+1)) <*> ("cd", 5) 
>("abcd", 6)

你可以清楚地看到使用结构Monoid&#34;(,)String&#34;在实现Applicative的这个实例时。

另一个显示&#34;结构Monoid&#34;使用:

Prelude Data.Monoid> (2::Integer,(+1)) <*> (1::Integer,5)

<interactive>:35:1: error:
    • Could not deduce (Monoid Integer) arising from a use of ‘<*>’
      from the context: Num b
        bound by the inferred type of it :: Num b => (Integer, b)
        at <interactive>:35:1-36
    • In the expression: (2 :: Integer, (+ 1)) <*> (1 :: Integer, 5)
      In an equation for ‘it’:
          it = (2 :: Integer, (+ 1)) <*> (1 :: Integer, 5)

3 个答案:

答案 0 :(得分:11)

“monoidal functor”所指的幺半群不是Monoid幺半群,即一个数值级的幺半群。它取而代之的是类型级monoid 。即,镗孔产品monoid

type Mempty = ()
type a <> b = (a,b)

(您可能会注意到这并不是严格意义上的幺半群;只有当您将((a,b),c)(a,(b,c))视为同一类型时才会这样。它们确实足够同构。)

要了解这与Applicative有何关系,请参阅。 monoidal仿函数,我们需要用其他术语来编写类。

class Functor f => Monoidal f where
  pureUnit :: f Mempty
  fzip :: f a -> f b -> f (a<>b)

-- an even more “general nonsense”, equivalent formulation is
-- upure :: Mempty -> f Mempty
-- fzipt :: (f a<>f b) -> f (a<>b)
-- i.e. the functor maps a monoid to a monoid (in this case the same monoid).
-- That's really the mathematical idea behind this all.

IOW

class Functor f => Monoidal f where
  pureUnit :: f ()
  fzip :: f a -> f b -> f (a,b)

根据Applicative定义标准Monoidal类的通用实例是一项简单的练习,反之亦然。

关于("ab",(+1)) <*> ("cd", 5):这与Applicative一般没有多大关系,但仅与作者应用具体相关。实例是

instance Monoid a => Monoidal ((,) a) where
  pureUnit = (mempty, ())
  fzip (p,a) (q,b) = (p<>q, (a,b))

答案 1 :(得分:10)

也许你正在寻找的幺半群就是这个。

newtype AppM f m = AppM (f m) deriving Show

instance (Applicative f, Monoid m) => Monoid (AppM f m) where
  mempty                      = AppM (pure mempty)
  mappend (AppM fx) (AppM fy) = AppM (pure mappend <*> fx <*> fy)

作为评论,在下面观察,它可以在名为Ap的{​​{3}}库中找到。它是Applicative的基础,所以让我们打开它。

特别注意,因为()只是MonoidAppM f ()也是Monoid。这就是潜伏在Applicative f背后的幺半群。

我们本可以坚持Monoid (f ())作为Applicative的超类,但这会对王室产生污染。

> mappend (AppM [(),()]) (AppM [(),(),()])
AppM [(),(),(),(),(),()]

Applicative []下面的幺半群是自然数的乘法,而列表的“明显”幺半群结构是连接,专门用于自然数的加法

数学警告。依赖类型警告。假Haskell警告。

了解正在发生的事情的一种方法是在依赖类型的雅培,Altenkirch和Ghani意义上考虑那些碰巧是容器的Applicative。我们很快就会在Haskell中拥有这些。我只是假装未来已经到来。

data (<|) (s :: *)(p :: s -> *) (x :: *) where
  (:<|:) :: pi (a :: s) -> (p a -> x) -> (s <| p) x

数据结构(s <| p)的特点是

  • 形状 s告诉您容器的外观。
  • 职位 p告诉您指定的形状,您可以在其中放置数据。

上述类型表示,为这样的结构提供数据是选择一个形状,然后用数据填充所有位置。

[]的容器演示文稿是Nat <| Fin其中

data Nat = Z | S Nat
data Fin (n :: Nat) where
  FZ :: Fin (S n)
  FS :: Fin n -> Fin (S n)

以便Fin n具有完全n个值。也就是说,列表的形状是 length ,它会告诉您填充列表需要多少元素。

您可以通过Functor f找到Haskell f ()的形状。通过使数据变得微不足道,这些位置并不重要。在Haskell中一般地构建GADT位置要困难得多。

Parametricity告诉我们

中容器之间的多态函数
forall x. (s <| p) x -> (s' <| p') x

必须由

提供
  • 将输入形状映射到输出形状的函数f :: s -> s'
  • 函数g :: pi (a :: s) -> p' (f a) -> p a映射(对于给定的输入形状)输出位置返回到输出元素将来自的输入位置。

morph f g (a :<|: d) = f a :<|: (d . g a)

(偷偷地说,我们这些接受过基本汉考克训练的人也会想到&#34;形状&#34; as&#34;命令&#34;和#34;位置&#34; as&#34;有效的回答&#34;。容器之间的态度恰好是&#34;设备驱动程序&#34;。但我离题了。)

按照类似的思路思考,制作容器Applicative需要做些什么?首先,

pure :: x -> (s <| p) x

等同于

pure :: (() <| Const ()) x -> (s <| p) x

必须由

提供
f :: () -> s   -- a constant in s
g :: pi (a :: ()) -> p (f ()) -> Const () a  -- trivial

某些

f = const neutral
neutral :: s

现在,

怎么样?
(<*>) :: (s <| p) (x -> y) -> (s <| p) x -> (s <| p) y

?同样,参数化告诉我们两件事。首先,计算输出形状的唯一有用数据是两个输入形状。我们必须有一个功能

outShape :: s -> s -> s

其次,我们用y填充输出位置的唯一方法是从第一个输入中选择一个位置以找到`x - &gt;中的函数。 ý&#39;然后在第二个输入中的位置以获得其参数。

inPos :: pi (a :: s)(b :: s) -> p (outShape a b) -> (p a, p b)

也就是说,我们总是可以识别确定输出位置输出的输入位置对。

适用法则告诉我们neutraloutShape必须遵守幺半群定律,而且,我们可以按如下方式提升幺半群

mappend (a :<|: f) (b :<|: g) = outShape a b :<|: \ z ->
  let (x, y) = inPos a b z
  in  mappend (f x) (g y)

此处还有更多要说的内容,但为此,我需要对容器上的两个操作进行对比。

<强>组合物

(s <| p) . (s' <| p')  =  ((s <| p) s') <| \ (a :<|: f) -> Sigma (p a) (p' . f)

其中Sigma是依赖对的类型

data Sigma (p :: *)(q :: p -> *) where
  Pair :: pi (a :: p) -> q a -> Sigma p q

这究竟是什么意思?

  • 你选择外形
  • 为每个外部位置选择内部形状
  • 复合位置是一对外部位置和一个适合内部形状的内部位置

或者,在汉考克

  • 您选择外部命令
  • 您可以在选择内部命令
  • 之前等待查看外部响应
  • 复合响应是对外部命令的响应,然后是对策略选择的内部命令的响应

或者,更加公然

  • 制作列表列表时,内部列表可以有不同的长度

join的{​​{1}}会使作文变平。潜伏在它背后不仅仅是形状上的幺半群,而是集成运算符。也就是说,

Monad

需要

join :: ((s <| p) . (s <| p)) x -> (s <| p) x

您的免费monad会为您提供策略树,您可以使用一个命令的结果来选择策略的其余部分。好像你在20世纪70年代的电传打字机上进行互动一样。

...同时

<强>张量

两个集装箱的张量(也是汉考克)是由

给出的
integrate :: (s <| p) s -> s

那是

  • 你选择了两种形状
  • 然后一个位置是一对位置,每个形状一个

  • 您选择了两个命令,但没有看到任何回复
  • 然后是一对响应

  • (s <| p) >< (s' <| p') = (s, s') <| \ (a, b) -> (p a, p' b) 矩形矩阵的类型:'内部'列表必须全部具有相同的长度

后者是一个线索,为什么[] >< []很难在Haskell中获取,但在依赖类型的设置中很容易。

与组合一样,张量是一个以身份函子作为其中性元素的幺半群。如果我们用张量替换><下面的构图,我们得到了什么?

Monad

但是pure :: Id x -> (s <| p) x mystery :: ((s <| p) >< (s <| p)) x -> (s <| p) x 可以做什么?这不是一个谜,因为我们知道在容器之间制作多态函数是一种相当严格的方法。必须有

mystery

这些正是我们之前所说的f :: (s, s) -> s g :: pi ((a, b) :: (s, s)) -> p (f (a, b)) -> (p a, p b) 所确定的。

<*>是由张量生成的有效编程的概念,其中Applicative由组合生成。您没有/需要等待外部响应来选择内部命令这一事实是Monad程序更容易并行化的原因。

Applicative视为矩形矩阵告诉我们为什么列表的[] >< []建立在乘法之上。

免费的applicative functor是带有旋钮的免费monoid。对于容器,

<*>

,其中

Free (s <| p) = [s] <| All p

所以&#34;命令&#34;是一个很大的命令列表,就像一副穿孔卡片。在选择卡片组之前,您无法看到任何输出。 &#34;响应&#34;是你的lineprinter输出。这是20世纪60年代。

所以你去吧。 All p [] = () All p (x : xs) = (p x, All p xs) 的本质,张量不是构成,需要一个潜在的幺半群,以及与幺半群相容的元素的重组。

答案 2 :(得分:1)

我想用在Monoid s中找到的Applicative的更多示例来补充Conor McBride(pigworker)的instructive answerIt has been observed某些函子的Applicative实例类似于相应的Monoid实例; 例如,我们有以下类推:

Applicative → Monoid
---------------------
List        → Product
Maybe       → All
Either a    → First a
State s     → Endo s

根据Conor的评论,我们可以理解为什么为什么有这些对应关系。 我们使用以下观察结果:

  1. 在应用程序操作Applicative下,Monoid容器的形状形成<*>
  2. 函子F的形状由F 1给出(其中1表示单位())。

对于上面列出的每个Applicative仿函数,我们通过使用unit元素实例化仿函数来计算其形状。 我们明白了...

List的形状为Nat

List a = μ r . 1 + a × r
List 1 = μ r . 1 + 1 × r
       ≅ μ r . 1 + r
       ≅ Nat

Maybe的形状为Bool

Maybe a = 1 + a
Maybe 1 = 1 + 1
        ≅ Bool

Either的形状为Maybe

Either a b = a + b
Either a 1 = a + 1
           ≅ Maybe a

State的形状为Endo

State s a = (a × s) ^ s
State s 1 = (1 × s) ^ s
          ≅ s ^ s
          ≅ Endo s

形状的类型与开头列出的Monoid下的类型完全匹配。 有一件事仍然让我感到困惑:其中一些类型允许多个Monoid实例( eg Bool可以被做成Monoid的{​​{1}}或All),但我不确定为什么我们要获得其中一个实例,而不是另一个。我的猜测是,这与适用法律以及它们如何与容器的其他组件(容器的位置)相互作用有关。