Haskell 自由幺半群和幺半群的主要区别是什么?

Haskell 自由幺半群和幺半群的主要区别是什么?,haskell,functional-programming,category-theory,monoids,Haskell,Functional Programming,Category Theory,Monoids,看起来我对Haskell中的幺半群有了相当清晰的理解,但上次我听说了一个叫做自由幺半群的东西 什么是自由幺半群,它与幺半群有什么关系 你能在Haskell中提供一个例子吗?自由幺半群是一种特定类型的幺半群。具体地说,它是通过将一些固定的元素集作为字符,然后从这些元素中形成所有可能的字符串而得到的幺半群。这些字符串(底层操作是字符串连接)形成一个幺半群,该幺半群称为自由幺半群。在编程上下文中,我通常将自由幺半群转换为[a]。Bartosz Milewski在关于的优秀系列文章中,在Haskell中

看起来我对Haskell中的
幺半群
有了相当清晰的理解,但上次我听说了一个叫做自由幺半群的东西

什么是自由幺半群,它与幺半群有什么关系


你能在Haskell中提供一个例子吗?

自由幺半群是一种特定类型的幺半群。具体地说,它是通过将一些固定的元素集作为字符,然后从这些元素中形成所有可能的字符串而得到的幺半群。这些字符串(底层操作是字符串连接)形成一个幺半群,该幺半群称为自由幺半群。

在编程上下文中,我通常将自由幺半群转换为
[a]
。Bartosz Milewski在关于的优秀系列文章中,在Haskell中将列表幺半群描述为列表幺半群(假设忽略了无限列表的一些问题)

标识元素为空列表,二进制操作为列表串联:

Prelude Data.Monoid> mempty :: [Int]
[]
Prelude Data.Monoid> [1..3] <> [7..10]
[1,2,3,7,8,9,10]
如果您以后决定将它们添加到一起,则有可能:

Prelude Data.Monoid> getSum $ mconcat $ Sum <$> [3,7]
10
Prelude Data.Monoid>getSum$mconcat$Sum[3,7]
10
如果您希望将它们相乘,也可以这样做:

Prelude Data.Monoid> getProduct $ mconcat $ Product <$> [3,7]
21
Prelude Data.Monoid>getProduct$mconcat$Product[3,7]
21
在这两个示例中,我特意选择将每个数字提升为包含更具体幺半群的类型(
Sum
Product
),然后使用
mconcat
执行聚合

对于加法和乘法,有更简洁的方法,但我这样做是为了说明如何使用更具体的幺半群来解释自由幺半群。

幺半群
(M,•,1)
是一种数学结构,如下所示:

  • M
    是一个集合
  • 1
    M
  • •:M*M->M
  • a•1=a=1•a
  • 给定
    M
    中的
    a
    b
    c
    元素,我们有
    a•(b•c)=(a•b)•c
  • M
    上的自由幺半群是一个幺半群
    (M',•,0)
    和函数
    e:M->M'
    ,对于任何幺半群
    (N,*,1)
    ,给定一个(集)映射
    f:M->N
    ,我们可以将其推广到一个幺半群同态
    f':(M',•,0)->(N,*,1)
    ,即

    f a = f' (e a)
    f' 0 = 1
    f' (a•b) = (f' a) • (f' b)
    
    换句话说,它是一个没有特殊作用的幺半群

    例如,幺半群是运算为加法且恒等式为0的整数。另一个幺半群是整数序列,其操作为串联,标识为空序列。现在加法下的整数不是整数上的自由幺半群。将该映射视为整数序列,采用<代码> n>代码> >代码>(n)< /代码>。然后,为了使这是免费的,我们需要将它扩展到一个将
    n+m
    映射到
    (n,m)
    ,也就是说,它必须将
    0
    映射到
    (0)
    (0,0)
    以及
    (0,0)
    等等

    另一方面,如果我们试着把整数序列看作是整数上的自由幺半群,我们会发现它在这种情况下似乎是有效的。将映射扩展为带加法的整数是一个取序列之和的扩展(其中()之和为0)

    那么集
    S
    上的自由幺半群是什么呢?我们可以尝试的一件事就是任意的
    S
    二叉树。在Haskell类型中,这看起来像:

    data T a = Unit | Single a | Conc (T a) (T a)
    
    它的标识为
    单位
    e=Single
    (•)=Conc

    我们可以编写一个函数来显示它是如何免费的:

    -- here the second argument represents a monoid structure on b
    free :: (a -> b) -> (b -> b -> b, b) -> T a -> b
    free f ((*),zero) = f' where
      f' (Single a) = f a
      f' Unit = zero
      f' (Conc a b) = f' a * f' b
    
    很明显,这满足了
    a
    上自由幺半群所需的定律。除了一个:
    ta
    不是幺半群,因为它不完全满足第4或第5定律

    所以现在我们应该问,我们是否可以把它变成一个更简单的自由幺半群,也就是一个实际的幺半群。答案是肯定的。一种方法是观察
    Conc Unit a
    Conc Unit
    单个a
    应相同。因此,让我们将前两种类型设置为不可表示:

    data TInner a = Single a | Conc (TInner a) (TInner a)
    data T a = Unit | Inner (TInner a)
    
    我们可以做的第二个观察是,
    Conc(Conc A b)c
    Conc A(Conc b c)
    之间应该没有区别。这是由于上述第5条。然后我们可以把树压平:

    data TInner a = Single a | Conc (a,TInner a)
    data T a = Unit | Inner (TInner a)
    
    Conc
    的奇怪结构迫使我们只能用一种方式来表示
    单个a
    单元
    。但是我们看到我们可以将这些合并在一起:将
    Conc
    的定义更改为
    Conc[a]
    ,然后我们可以将
    Single x
    更改为
    Conc[x]
    ,将
    Unit
    更改为
    Conc[]
    ,因此我们有:

    data T a = Conc [a]
    
    或者我们可以写:

    type T a = [a]
    
    行动包括:

    unit = []
    e a = [a]
    (•) = append
    
    free f ((*),zero) = f' where
      f' [] = zero
      f' (x:xs) = f x * f' xs
    

    因此在Haskell中,列表类型被称为自由幺半群。

    正如您已经知道的,幺半群是一个集合,包含一个元素
    e
    和一个操作

    e <> x = x <> e = x  (identity)
    (x<>y)<>z = x<>(y<>z)  (associativity)
    
    这不是前两个方程的结果

    注意,在数学中可以证明,所有自由幺半群同构于列表幺半群
    [a]
    。因此,编程中的“自由幺半群”对于任何数据结构来说只是一个花哨的术语,1)可以转换为列表,然后返回,不会丢失信息;2)反之亦然,列表可以转换为列表,然后返回,不会丢失信息


    在Haskell中,你可以用“类似列表的类型”替换“自由幺半群”。

    这是一个数学问题吗?也许它更适合@Coderino-Javarino,我更喜欢从编程的角度给出答案。我想我没有足够的能力去理解数学人的反应。如果我找不到回应,我一定会去那里试试。谢谢编程的观点是,这不是一个编程问题:-)(范畴理论是为理论计算机科学家而提出的。)事实上,幺半群起源于m
    e <> x = x <> e = x  (identity)
    (x<>y)<>z = x<>(y<>z)  (associativity)
    
    x<>y = y<>x