为什么选择 Fennel?

Fennel 是一种在 Lua 运行时上运行的编程语言。

为什么选择 Lua?

Lua 编程语言是一个出色且被低估的工具。它非常强大,但在概念上(作为一种语言)以及在实现规模上都保持着非常小的 footprint。(参考实现包含大约 19000 行 C 代码,编译后大小为 278kb。)部分原因是它非常简单,Lua 也非常快。但关于 Lua 最重要的一点是,它专门设计用于嵌入到其他程序中,以便让最终用户可以重新编程。

Lua 在概念上的简洁性与其他“易于学习”的语言(如 JavaScript 或 Python)形成鲜明对比——Lua 包含了完成工作所需的最小数量的想法;只有 Forth 和 Scheme 提供了类似的简洁性。当你将这种严谨的简洁性与强调程序的可重新编程性结合起来时,其结果是对抗当前技术趋势的有力解毒剂,这些趋势将程序视为用户无法控制的黑盒。

然而...

如果 Lua 如此出色,为什么不直接使用 Lua 呢?在许多情况下,你应该这样做!但是,Lua 存在一些缺点,随着时间的推移,这些缺点已被证明容易出错或不清楚。Fennel 在 Lua 上运行,并且 Fennel 的运行时语义是 Lua 的一个子集,但你可以将 Fennel 视为一种可以用来编写 Lua 程序的替代表示法,它可以帮助你避免常见的陷阱。这使得 Fennel 可以专注于把一件事做得很好,而不会因为实现虚拟机、标准库、分析器和调试器之类的事情而被拖累。任何已经适用于 Lua 的库或工具也同样适用于 Fennel。

Lua 和 Fennel 之间最明显的区别是带括号优先的语法;Fennel 属于 Lisp 编程语言家族。你可以说这消除了语法的复杂性;基于括号的语法更规则,并且边缘情况更少。仅仅因为是一种 Lisp,Fennel 从 Lua 中移除了:

变量

对 Lua 最常见的合理批评之一是,它很容易意外地使用全局变量,要么是因为忘记添加 local 声明,要么是因为输入错误。Fennel 允许你在极少数需要全局变量的情况下使用它们,但使得意外使用它们非常困难。

Fennel 还移除了重新分配普通局部变量的能力。如果你声明一个将被重新分配的变量,你必须使用 var 来引入它。这鼓励编写更简洁的代码,并且可以一目了然地看出何时会发生重新分配。请注意,Lua 5.4 引入了一个类似的想法,即使用 <const> 变量,但由于 Fennel 不必像 Lua 那样保留数十年的现有代码,因此它能够使更简洁的选择成为默认选项,而不是选择加入。

表和循环

Lua 用于表(其数据结构)的表示法感觉有些过时。它使用花括号来表示顺序(类似数组)表和键/值(类似字典)表,而 Fennel 使用更熟悉的表示法,即使用方括号表示顺序表,使用花括号表示键/值表。

此外,Lua 重载了 for 关键字,用于数值“从 X 到 Y 计数”样式的循环以及更通用的基于迭代器的循环。Fennel 在第一种情况下使用 for,并在后一种情况下引入了 each 形式。

函数

对 Lua 的另一个常见批评是它缺乏参数个数检查;也就是说,如果你在没有足够参数的情况下调用函数,它将直接执行,而不是指示错误。Fennel 允许你编写以这种方式工作的函数 (fn),当需要速度时,但它也允许你编写使用 lambda 检查其期望的参数的函数。

其他

如果你一直在使用较新的语言进行编程,你可能会因绑定变量时普遍存在的解构数据结构以及用于编写更具声明性的条件语句的模式匹配而感到惊喜。这些在 Lua 中都缺失了,但在 Fennel 中包含了。

最后,Fennel 包含一个宏系统,因此你可以轻松地扩展该语言以包含新的语法形式。此功能被故意列在最后,因为虽然 Lisp 程序员历来都很看重它有多强大,但很少遇到证明如此强大的构造是合理的的情况。

要从设计的角度更详细地了解 Fennel 的指导原则,请参阅 Fennel 的价值观

Home source for this site