滥用代数数据类型的代数 - 为什么这样做?

时间:2012-02-08 09:09:50

标签: haskell functional-programming ocaml algebraic-data-types miranda

代数数据类型的“代数”表达式对于具有数学背景的人来说非常具有启发性。让我试着解释一下我的意思。

定义了基本类型

  • 产品
  • 联盟+
  • Singleton X
  • 单位1

并使用的简写X•X2XX+X,我们可以定义代数表达式,例如链表

data List a = Nil | Cons a (List a)L = 1 + X • L

和二叉树:

data Tree a = Nil | Branch a (Tree a) (Tree a)T = 1 + X • T²

现在,我作为数学家的第一直觉是坚持使用这些表达方式,并尝试解决LT。我可以通过重复替换来做到这一点,但似乎更容易滥用符号,并假装我可以随意重新排列。例如,对于链接列表:

L = 1 + X • L

(1 - X) • L = 1

L = 1 / (1 - X) = 1 + X + X² + X³ + ...

我使用1 / (1 - X)的幂级数展开以完全不合理的方式得出一个有趣的结果,即L类型是Nil,或者它包含1元素,或者它包含2个元素,或3等。

如果我们为二叉树做这件事会更有趣:

T = 1 + X • T²

X • T² - T + 1 = 0

T = (1 - √(1 - 4 • X)) / (2 • X)

T = 1 + X + 2 • X² + 5 • X³ + 14 • X⁴ + ...

再次使用幂级数展开(使用Wolfram Alpha完成)。这表达了非显而易见的(对我来说)事实,即只有一个二元树有1个元素,2个二叉树有两个元素(第二个元素可以在左边或右边的分支上),5个二叉树有三个元素等

所以我的问题是 - 我在这做什么?这些操作似乎没有道理(无论如何,代数数据类型的平方根究竟是什么?)但它们会产生明显的结果。两种代数数据类型的商是否在计算机科学中有任何意义,还是仅仅是符号诡计?

而且,或许更有趣的是,是否可以扩展这些想法?是否存在类型代数的理论,例如,允许类型上的任意函数,或类型是否需要幂级数表示?如果你可以定义一类函数,那么函数的组合是否有任何意义?

5 个答案:

答案 0 :(得分:43)

二叉树在类型的半环中由等式T=1+XT^2定义。通过构造,T=(1-sqrt(1-4X))/(2X)由复数的半环中的相同方程定义。因此,考虑到我们在同一类代数结构中求解相同的方程式,我们看到一些相似之处实际上并不令人惊讶。

问题在于,当我们在复杂数字的半环中推理多项式时,我们通常使用复数形成环或甚至是场的事实,因此我们发现自己使用不适用于半环的减法等操作。但是,如果我们有一条允许我们从方程的两边取消的规则,我们通常可以从我们的论证中消除减法。这是Fiore and Leinster证明的有关环的许多论点可以转移到半环的事情。

这意味着您对戒指的大量数学知识可以可靠地转移到类型上。因此,一些涉及复数或幂级数的论证(在正式权力系列的环中)可以以完全严格的方式转移到类型。

然而,这个故事还有更多。通过显示两个幂级数相等来证明两种类型相等(比如说)是一回事。但您也可以通过检查电源系列中的术语来推断有关类型的信息。我不确定这里的正式声明应该是什么。 (我推荐paper上的Brent Yorgey combinatorial species进行一些密切相关的工作,但物种 与类型相同。)

我发现完全令人兴奋的是你发现的东西可以扩展到微积分。关于微积分的定理可以转移到类型的半环节。事实上,即使关于有限差分的论证也可以转移,你会发现数值分析中的经典定理在类型理论中有解释。

玩得开心!

答案 1 :(得分:20)

似乎您所做的只是扩展递归关系。

L = 1 + X • L
L = 1 + X • (1 + X • (1 + X • (1 + X • ...)))
  = 1 + X + X^2 + X^3 + X^4 ...

T = 1 + X • T^2
L = 1 + X • (1 + X • (1 + X • (1 + X • ...^2)^2)^2)^2
  = 1 + X + 2 • X^2 + 5 • X^3 + 14 • X^4 + ...

由于类型操作的规则就像算术运算规则一样,你可以使用代数方法来帮助你弄清楚如何扩展递归关系(因为它不是很明显)。

答案 2 :(得分:18)

我没有完整的答案,但这些操作倾向于“正常工作”。相关论文可能是Objects of Categories as Complex Numbers by Fiore and Leinster - 我在阅读sigfpe's blog on a related subject时遇到过那篇论文。该博客的其余部分是类似想法的金矿,值得一试!

顺便说一下,您还可以区分数据类型 - 这将为您提供适合数据类型的Zipper!

答案 3 :(得分:10)

通信过程代数(ACP)处理类似的过程表达式。 它提供了加法和乘法作为选择和序列的运算符,以及相关的中性元素。 基于这些,有其他构造的运算符,例如并行和中断。 见http://en.wikipedia.org/wiki/Algebra_of_Communicating_Processes。还有一篇名为“过程代数简史”的在线论文。

我正致力于使用ACP扩展编程语言。去年四月,我在2012年Scala Days上发表了一篇研究论文,可在http://code.google.com/p/subscript/

获取

在会议上,我演示了一个运行并行递归规范的调试器:

Bag = A; (袋及安培;一个)

其中A和a代表输入和输出动作;分号和&符号代表顺序和并行。请参阅SkillsMatter上的视频,可从上一个链接访问。

更符合

的行李规格

L = 1 + X•L

将是

B = 1 + X& B

ACP使用公理在选择和顺序方面定义并行性;请参阅维基百科文章。我想知道包包的类比是什么

L = 1 /(1-X)

ACP样式编程对于文本解析器和GUI控制器很方便。

等规格

searchCommand = clicked(searchButton)+ key(Enter)

cancelCommand = clicked(cancelButton)+ key(Escape)

通过使两个细化“clicked”和“key”隐式(比如Scala允许的函数),可以更简洁地写下

。因此我们可以写:

searchCommand = searchButton + Enter

cancelCommand = cancelButton + Escape

右侧现在包含作为数据的操作数,而不是进程。在这个级别,没有必要知道隐式改进将把这些操作数转换为进程;他们不一定会改进投入行动;输出动作也适用,例如在测试机器人的规范中。

进程以数据的形式获取数据;因此我用“项目代数”这个词来表达。

答案 4 :(得分:6)

具有

类型的微积分和Maclaurin系列

这是另一个小的补充 - 组合扩展中系数扩展的系数为什么应该工作的组合洞察,特别是关注可以使用微积分的Taylor-Maclaurin方法导出的系列。注意:您给出的操纵列表类型的示例系列扩展是Maclaurin系列。

由于其他答案和评论涉及代数类型表达式(总和,产品和指数)的行为,因此这个答案将忽略该细节并专注于类型' calculus'。

在这个答案中你可能会注意到引号中的引号很重要。有两个原因:

  • 我们从一个域向另一个域的实体提供解释,并且以这种方式划分这样的外国概念似乎是恰当的。
  • 一些概念可以更严格地形式化,但形状和想法似乎比细节更重要(并且占用的空间更少)。

Maclaurin系列的定义

函数f : ℝ → ℝ的{​​{3}}定义为

f(0) + f'(0)X + (1/2)f''(0)X² + ... + (1/n!)f⁽ⁿ⁾(0)Xⁿ + ...

其中f⁽ⁿ⁾表示n的{​​{1}}衍生物。

为了能够理解用类型解释的Maclaurin系列,我们需要理解我们如何在类型上下文中解释三件事:

  • a(可能是多个)衍生物
  • 将函数应用于f
  • 0
  • 这样的字词

事实证明,分析中的这些概念在类型世界中具有合适的对应物。

对于合适的对手来说,我的意思是什么?它应该具有同构的味道 - 如果我们能够在两个方向上保存真理,那么在一个上下文中可推导出的事实可以转移到另一个上下文。

类型

的微积分

那么类型表达式的导数是什么意思呢?事实证明,对于一个大型且表现良好(可区分的')类型的表达式和仿函数,有一种自然的操作,其行为类似于足以成为合适的解释!

为了破坏妙语,类似于差异化的操作就是制造单洞背景'。 Maclaurin series是进一步拓展这一特定点的绝佳场所,但单孔上下文((1/n!))的基本概念是它表示提取特定类型的单个子项的结果({ {1}})来自术语(类型da/dx),保留所有其他信息,包括确定子项的原始位置所必需的信息。例如,表示列表的单孔上下文的一种方法是使用两个列表:一个用于在提取的项目之前的项目,一个用于之后的项目。

通过区分识别该操作的动机来自以下观察。我们写x来表示类型为a的类型为da/dx的单孔上下文的类型。

a

此处,xd1/dx = 0 dx/dx = 1 d(a + b)/dx = da/dx + db/dx d(a × b)/dx = a × db/dx + b × da/dx d(g(f(x))/dx = d(g(y))/dy[f(x)/a] × df(x)/dx 分别代表只有一个且完全为零的居民的类型,10代表通常的总和和产品类型。 +×用于表示类型函数或类型表达式,f表示在前面的表达式中用g替换每个[f(x)/a]的操作

这可以用无点样式编写,写f(x)来表示函数a的派生函数,因此:

f'

这可能是优选的。

如果我们使用类型和仿函数的同构类来定义导数,那么可以使等式变得严格和准确。

现在,我们特别注意到微积分中有关加法,乘法和合成的代数运算(通常称为求和,乘积和链条规则)的规则完全由'的运算反映出来。打个洞#39;此外,“打洞”的基本情况是在一个常量表达式中,或者术语f本身也表现为区分,因此通过归纳,我们可以获得所有代数类型表达式的类似区分的行为。

现在我们可以解释差异化,(x ↦ 1)' = x ↦ 0 (x ↦ x)' = x ↦ 1 (f + g)' = f' + g' (f × g)' = f × g' + g × f' (g ∘ f)' = (g' ∘ f) × f' '衍生物'类型表达式x是什么意思?它是一种代表n的类型 - 地点上下文:当填充时的条款'使用dⁿe/dxⁿ类型n的条款产生n。还有另一个与' x'相关的重要观察结果。来晚了。

类型仿函数的不变部分:将函数应用于0

我们已经在类型世界中对e进行了解释:没有成员的空类型。从组合的角度来看,将类型函数应用于它是什么意思?更具体地说,假设(1/n!)是一个类型函数,0是什么样的?好吧,我们当然无法访问f类型的任何内容,因此任何需要f(0)的{​​{1}}结构都不可用。剩下的是那些在他们缺席的情况下可以访问的术语,我们可以将其称为“不变”。或者'常数'部分类型。

对于一个明确的示例,请使用0仿函数,该仿函数可以代数表示为f(x)。当我们将此应用于x时,我们得到Maybe - 它就像x ↦ 1 + x一样:唯一可能的值是0值。对于列表,类似地,我们只获得与空列表对应的术语。

当我们将其归还并将类型1 + 0解释为数字时,可以将其视为 count ,其中包含1类型的任意数量(对于任何{可以在无法访问None的情况下获取{1}}),即“空白”的数量为'术语

把它放在一起:完整解释Maclaurin系列

我担心我无法将f(0)作为一种类型直接解释。

如果我们根据上述情况考虑类型f(x),我们会发现它可以被解释为x的类型 - 类型为{{1}的术语的上下文} 尚未包含 x - 也就是说,当我们整合'他们(1/n!)次,生成的字词完全 f⁽ⁿ⁾(0) n s,不多也不少。然后将类型f(x)解释为数字(如Maclaurin系列x中的系数)只是计算有多少这样的空n - 位置上下文。我们快到了!

n最终会在哪里?检查类型分化的过程'告诉我们,当多次应用时,它会保留“订单”。其中提取子项。例如,考虑x类型的术语f⁽ⁿ⁾(0)以及“挖洞”的操作'在它两次。我们得到两个序列

f

尽管两者都来自同一个词,因为有n个方法从两个元素中取两个元素,保留顺序。一般来说,This方法可以从(1/n!)中获取(x₀, x₁)个元素。因此,为了计算具有x × x元素的仿函数类型的配置数量,我们必须计算类型(x₀, x₁) ↝ (_₀, x₁) ↝ (_₀, _₁) (x₀, x₁) ↝ (x₀, _₀) ↝ (_₁, _₀) (where _ represents a 'hole') 并除以2! = 2完全< / em>与Maclaurin系列的系数一样。

因此除以n结果只能解释为其本身。

最后的想法:&#39;递归&#39;定义和分析

首先,一些观察结果:

  • 如果函数f:ℝ→ℝ具有导数,则该导数是唯一的
  • 类似地,如果函数f:ℝ→ℝ是解析的,它只有一个相应的多项式系列

由于我们有链规则,如果我们将类型导数形式化为同构类,我们可以使用there are n!。但隐式分化并不需要任何外来机动,如减法或除法!所以我们可以用它来分析递归类型定义。以列表为例,我们有

n

然后我们可以评估

n

获得Maclaurin系列中的f⁽ⁿ⁾(0)系数。

但是,既然我们确信这些表达确实是严格的可区分的,如果只是隐含的,并且因为我们与函数ℝ→correspondence有对应关系,其中衍生物当然是唯一的,我们可以放心即使我们使用&#39;非法&#39;操作,结果有效。

现在,类似地,使用第二个观察,由于函数ℝ→correspondence的对应关系(它是同态吗?),我们知道,只要我们对函数具有 a感到满意Maclaurin系列,如果我们能够找到任何系列,可以应用上述原则使其严谨。

关于你关于函数组合的问题,我认为链规则提供了部分答案。

我不确定这适用于多少Haskell风格的ADT,但我怀疑它有很多,如果不是全部的话。我发现了这个事实的一个真正奇妙的证据,但是这个边际太小而不能包含它......

现在,当然,这只是解决这里发生的事情的一种方法,可能还有很多其他方法。

摘要:TL; DR

  • 类型&#39;分化&#39;对应于&#39; implicit differentiation&#39;。
  • 将一个仿函数应用于n!,让我们得到一个空洞的&#39;该仿函数的术语。
  • 因此,
  • making a hole幂系列(有些)严格对应于枚举具有一定数量元素的仿函数类型的成员数。
  • Maclaurin让这更加无懈可击。
  • 衍生品的独特性和幂级数的独特性意味着我们可以捏造细节并且它有效。