这是一个关于 APL 解释器的项目总结,主要内容如下:
- APL 简介:APL 是一种数组编程语言,其唯一数据类型是数组。作者被其语法吸引,代码简洁且表达力强。学习 APL 编程需要不同的思维方式,它类似于函数式编程,但更鼓励依赖全局属性和泛操作。
- 选择 Haskell 的原因:作者原本计划深入研究 APL,学习 Haskell 只是附带任务,但最终发现学习使用 Haskell 是项目中最困难的部分。Haskell 对于数组语言解释器来说可能不是理想的工具,虽然它使函数的解析和组合更优雅,但牺牲了处理状态、数据结构和性能的便利性。
- 整体架构:程序的工作方式类似于一般的解释器,包括读取输入文本、转换为标记、构建语法树、评估语法树、打印结果等步骤,解释器状态在整个过程中被读取和更新。
解析过程:
- 版本 1(上下文无关):核心逻辑单元是
MatchFn
,它接受一个标记列表作为输入,可能返回匹配的内容和新的标记列表。通过一系列辅助函数(如matchOne
、matchAll
等)来组合MatchFn
,实现对非终端的解析。 - 版本 2(添加上下文):由于 APL 没有上下文无关语法,
MatchFn
需要添加全局状态(IdMap
)作为参数,只有终端匹配函数和辅助函数需要更改,非终端解析函数不需要直接接触MatchFn
的参数。 - 版本 3(添加 monads):在编写和重构解析器的过程中,作者发现了一些不便之处,通过将
MatchFn
改为一个 monad,使用通用的 monadic 函数代替chFst
和mchFst
,解决了这些问题。 - 版本 4(添加 Applicative):Haskell 中的所有 Monads 也是 Applicative Functors,使用
<*>
、<*>
和(*>)
等函数可以去除MatchT
函数和大部分 lambda 表达式,使代码更简洁。
- 版本 1(上下文无关):核心逻辑单元是
- 评估过程:项目范围较大,语法树的评估有很多细微之处。例如,APL 中函数作为数据很自然,在 Haskell 中也可以将函数存储为正常数据。评估使用 monads 来处理状态,通过定义
SubEvalM
类型类来处理不同的函数类型。APL 还有“选择性赋值”的特性,可以通过替换数组和应用选择函数来实现。处理高维数组需要一些核心函数,如alongAxis
、alongRank
和arrReorderAxes
,它们主要用于索引算术和形状操作。 - 模仿 Dyalog:该项目基于 Dyalog APL,语法和符号直接来自 Dyalog,但在打印数组、函数树等方面与 Dyalog 存在差异,作者在实现过程中做出了很多妥协。
Haskell 的优缺点:
- 优点:编译器提供了很多保证,减少了运行时错误;标准库中有很多有用的函数,使用标准函数可以避免编写大量样板代码;Currying、Combinators 和 Tacit 等特性很有趣。
- 缺点:学习曲线陡峭,对于不熟悉函数式编程和类型系统的人来说很难理解;某些库(如 System.Random)过于通用,理解和使用起来困难;在性能方面表现不佳,优化 Haskell 代码可能是徒劳的;懒求值使得调试更加困难,错误的出现时间不确定,捕捉错误也很困难。
总的来说,APL 解释器的实现过程涉及到很多复杂的技术和概念,Haskell 的特性既带来了便利,也带来了挑战。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。