滥用代数数据类型的代数 - 为什么这有效?

2024-01-09

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

定义了基本类型

  • Product
  • Union +
  • 辛格尔顿X
  • Unit 1

并使用简写 for X•X and 2X for X+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²

现在,作为一名数学家,我的第一本能就是对这些表达式发疯,并尝试解决L and T。我可以通过重复替换来做到这一点,但可怕地滥用该符号并假装我可以随意重新排列它似乎更容易。例如,对于链表:

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⁴ + ...

再次,使用幂级数展开(完成沃尔夫勒姆·阿尔法 http://www.wolframalpha.com/input/?i=%281+-+sqrt%281-4x%29%29+%2F+%282x%29)。这表达了一个不明显的(对我来说)事实:只有一棵有 1 个元素的二叉树,2 个有两个元素的二叉树(第二个元素可以在左侧或右侧分支),5 个有三个元素的二叉树等。

所以我的问题是——我在这里做什么?这些操作看起来不合理(代数数据类型的平方根究竟是什么?),但它们会产生合理的结果。两种代数数据类型的商在计算机科学中是否有任何意义,或者只是符号技巧?

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


免责声明:当你考虑 ⊥ 时,很多东西并不能真正正常工作,所以为了简单起见,我将公然忽略它。

初步的几点:

  • 请注意,“联合”可能不是这里 A+B 的最佳术语——具体来说a disjoint union http://en.wikipedia.org/wiki/Disjoint_union两种类型的区别,因为即使类型相同,两侧也是有区别的。无论如何,更常见的术语就是“总和类型”。

  • 实际上,单例类型是所有单元类型。它们在代数运算下的行为相同,更重要的是,仍然保留了存在的信息量。

  • 您可能还想要一个零类型。 Haskell 规定为Void。不存在类型为零的值,就像存在一个类型为一的值一样。

这里还缺少一项重大操作,但我稍后会再讲。

正如您可能已经注意到的,Haskell 倾向于借用范畴论中的概念,并且上述所有内容都有一个非常简单的解释:

  • 给定对象 A 和 BHask,他们的乘积 A×B 是允许两个投影的唯一(同构)类型fst: A×B → A 和snd: A×B → B,其中给定任何类型 C 和函数f:C→A,g: C → B 您可以定义配对f&&&g: C → A×B 使得fst ∘ (f &&& g) = f同样对于g。参数化自动保证了通用属性,我对名称的不那么微妙的选择应该能让你明白。这(&&&)运算符定义在Control.Arrow, 顺便一提。

  • 上面的对偶是带有注射的副产品 A+Binl: A → A+B 且inr: B → A+B,其中给定任何类型 C 和函数f:A→C,g:B→C,可以定义配对f ||| g: A+B → C 使得明显的等价成立。同样,参数化自动保证了大多数棘手的部分。在这种情况下,标准注射很简单Left and Right配对是函数either.

乘积和求和类型的许多属性都可以从上面导出。请注意,任何单例类型都是终端对象Hask任何空类型都是初始对象。

Returning to the aforementioned missing operation, in a cartesian closed category http://en.wikipedia.org/wiki/Cartesian_closed_category you have exponential objects http://en.wikipedia.org/wiki/Exponential_object that correspond to arrows of the category. Our arrows are functions, our objects are types with kind *, and the type A -> B indeed behaves as BA in the context of algebraic manipulation of types. If it's not obvious why this should hold, consider the type Bool -> A. With only two possible inputs, a function of that type is isomorphic to two values of type A, i.e. (A, A). For Maybe Bool -> A we have three possible inputs, and so on. Also, observe that if we rephrase the copairing definition above to use algebraic notation, we get the identity CA × CB = CA+B.

As for why这一切都是有道理的——特别是为什么你使用幂级数展开式是合理的——请注意,上面的大部分内容都指的是某种类型的“居民”(即具有该类型的不同值),以证明代数行为。为了明确这一观点:

  • 产品类型(A, B)代表一个值,每个来自A and B,独立拍摄。所以对于任何固定值a :: A, 有一个类型值(A, B)对于每个居民B。这当然是笛卡尔乘积,乘积类型的居民数是各因子的居民数的乘积。

  • 总和类型Either A B代表一个值A or B,区分左右分支。前面提到,这是一个不相交并集,sum类型的居民数是被加数的居民数之和。

  • 指数型B -> A表示类型值的映射B到类型的值A。对于任何固定参数b :: B,任何值A可以分配给它;类型的值B -> A为每个输入选择一个这样的映射,这相当于多个副本的乘积A as B有居民,因此求幂。

虽然一开始很想将类型视为集合,但实际上在这种情况下效果并不好——我们有不相交的并集而​​不是标准的集合并集,对交集或许多其他集合运算没有明显的解释,而且我们通常不关心集合成员资格(将其留给类型检查器)。

另一方面,上面的结构花了很多时间谈论counting居民,以及列举类型的可能值在这里是一个有用的概念。这很快就会导致我们枚举组合数学 http://en.wikipedia.org/wiki/Enumerative_combinatorics,如果您查阅链接的维基百科文章,您会发现它所做的第一件事就是定义“对”和“并”,其含义与乘积和总和类型完全相同生成函数 http://en.wikipedia.org/wiki/Generating_function,然后使用与您所做的完全相同的技术对与 Haskell 列表相同的“序列”执行相同的操作。


Edit:哦,我认为这里有一个快速的奖励,它引人注目地证明了这一点。您在评论中提到对于树类型T = 1 + T^2你可以得出身份T^6 = 1,这显然是错误的。然而,T^7 = T does成立,树和树的七元组之间的双射可以直接构造,参见。安德烈亚斯·布拉斯的《七树合一》 http://www.math.lsa.umich.edu/~ablass/comb.html.

Edit×2:关于其他答案中提到的“类型的导数”结构的主题,您可能还会喜欢这篇论文来自同一作者 https://personal.cis.strath.ac.uk/~conor/Dissect.pdf它进一步建立在这个想法的基础上,包括除法和其他有趣的东西的概念。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

滥用代数数据类型的代数 - 为什么这有效? 的相关文章

  • 是否可以列出派生 Generic 的记录数据类型中字段的名称和类型?

    我知道对于派生 Data Data 的数据类型 constrFields http hackage haskell org package base 4 7 0 2 docs Data Data html v constrFields给出字
  • 用parsec解析递归数据

    import Data Attoparsec Text Lazy import Data Text Lazy Internal Text import Data Text Lazy pack data List a Nil Cons a L
  • 如何在 Yesod 中使用 CSS 框架?

    我想将 Blueprint CSS 框架与 Yesod 一起使用 有没有最佳实践 因为 Yesod 使用 CSS 模板 所以在我看来我不能直接使用 css 文件 我必须将它们重命名为 lucius files 吗 如何将 CSS 添加到 d
  • f# 运行总计序列

    好吧 这看起来应该很容易 但我就是不明白 如果我有一个数字序列 如何生成由运行总计组成的新序列 例如 对于序列 1 2 3 4 我想将其映射到 1 3 6 10 以适当的功能方式 Use List scan https msdn micro
  • 将系统命令的结果绑定到 Haskell 中的变量

    如何在 Haskell 中运行系统命令and将其结果 即标准输出 绑定到变量 在伪 Haskell 中 我正在寻找类似以下内容的内容 import System Process main do output lt callCommand e
  • 访问函数中的环境

    In main我可以读取我的配置文件 并将其提供为runReader somefunc myEnv正好 但somefunc不需要访问myEnv读者提供 链中的下一对也没有提供 需要 myEnv 中某些内容的函数是一个微小的叶函数 如何在不将
  • 导入 Haskell 模块

    我是哈斯克尔的新手 为什么当我尝试使用时Days from Data Time我收到此错误 Could not find module Data Time It is a member of the hidden package time
  • Haskell 中的前提条件检查有哪些选项

    这是一个简单的问题 我认为答案很复杂 一个非常常见的编程问题是函数返回某些内容 或者前置条件检查失败 在Java中 我会使用一些抛出异常的断言函数IllegalArgumentException在方法的开头 如下所示 method body
  • 如何在 Perl 中以函数式风格进行编码?

    你如何 have a sub返回一个sub or 将文本作为代码执行 in Perl 另外 如何拥有匿名函数存储状态 子返回子作为coderef example 1 return a sub that is defined inline s
  • OCaml 作为 C 库,hello world 示例

    我希望通过 C 调用 OCaml 代码 方法是将 OCaml 编译为包含 C 接口的静态或共享库 这一页 https caml inria fr pub docs manual ocaml intfc html似乎解释了如何为 OCaml
  • 如何手动推断表达式的类型

    给定 Haskell 函数 head filter fst 现在的问题是如何手动 手动 找到类型 如果我让 Haskell 告诉我我得到的类型 head filter fst Bool b gt Bool b 但我想了解仅使用所用函数的签名
  • Haskell 下划线与显式变量

    我已经学习 Haskell 几个星期了 我有一个关于下划线的使用的问题 作为函数参数 我认为用一个具体的例子来问我的问题会更好 假设我想定义一个函数 根据提供的索引提取列表的元素 是的 我意识到 已经是预先定义的 我可以定义该函数的两种方法
  • 持久 selectList 导致错误“无法将类型‘BaseBackend backend0’与‘SqlBackend’匹配”

    我遇到以下编译错误 Couldn t match type BaseBackend backend0 with SqlBackend arising from a use of runSqlite The type variable bac
  • Haskell:是的,没有类型类。为什么是整数?

    我有一个关于 GHCi 如何假定整数类型的问题 我正在阅读 Learn you a Haskell 是 否类型的课程 如果您想阅读全文 这里有一个链接 http learnyouahaskell com making our own typ
  • 在依赖类型的函数式编程语言中,扁平化列表是否更容易?

    在 haskell 中寻找一个可以展平任意深度嵌套列表的函数时 即应用的函数concat递归并在最后一次迭代时停止 使用非嵌套列表 我注意到这需要有一个更灵活的类型系统 因为随着列表深度的变化 输入类型也会变化 确实 有几个 stackov
  • 规范化且不可变的数据模型

    Haskell如何解决 规范化不可变数据结构 问题 例如 让我们考虑一个表示前女友 男友的数据结构 data Man Man name String exes Woman data Woman Woman name String exes
  • “Eta减少”并不总是在Haskell中举行?

    我发现我可以说 LANGUAGE RankNTypes f1 forall b b gt b gt forall c c gt c f1 f id f HLint 告诉我我可以在这里做 Eta 减少 但是 f2 forall b b gt
  • Vim 脚本中的“reduce”函数

    Vim 脚本有一些非常基本的函数式编程工具 It has map and filter 但据我所知它缺乏reduce 功能 Reduce https en wikipedia org wiki Fold 28higher order fun
  • 是否可以只迭代一个流一次并执行 2 个或更多操作?

    给定代码 List
  • Haskell 入门

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 几天来 我一直试图理解 Haskell 中的函数式编程范例 我通过阅读教程和观看截屏视频

随机推荐