(我改写原来的答案,因为它产生了太多的误解)
保持 Clojure(和任何 Lisp)动态类型的原因之一是简化宏的创建。简而言之,宏处理抽象语法树(AST),它可以包含许多不同类型的节点(通常是任何对象)。理论上,可以制作完整的静态类型宏系统,但在实践中,此类系统通常受到限制且分布稀疏。请参阅下面的示例和线程中的扩展讨论。
编辑 2020:哇,从我发布这个答案的时间已经过去了 9 年,人们仍然添加评论。我们都留下了怎样的遗产!
有些人在评论中指出,拥有静态类型语言并不会阻止您将代码表达为数据结构。而且,严格来说,这是真的——联合类型允许表达任何复杂的数据结构,包括语言的语法。然而,我声称要表达语法,你必须要么降低表达能力,要么使用如此广泛的联合,以至于你失去了静态类型的所有优势。为了证明这一说法,我将使用另一种语言——Julia。
Julia 是可选类型的——您可以将任何函数或结构字段限制为具有特定类型,Julia 会检查它。Expr
该语言支持 AST 作为使用和Symbol
类型的一等公民。表达式定义如下所示:
struct Expr
head::Symbol
args::Vector{Any}
end
表达式由一个始终是符号的头和可以具有任何类型的参数列表组成。Julia 还支持 specialUnion
可以将参数限制为特定类型,例如Symbol
s 和 other Expr
s:
struct Expr
head::Symbol
args::Vector{Union{Symbol, Expr}}
end
这足以表达例如:(x + y)
:
dump(:(x + y))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Symbol x
3: Symbol y
但是 Julia 还支持许多其他类型的表达式。一个明显且有用的示例是文字:
:(x + 1)
此外,您可以手动使用插值或构造表达式将任何对象放入 AST:
obj = create_some_object()
ex1 = :(x + $objs)
ex2 = Expr(:+, :x, obj)
这些示例不仅仅是一个有趣的实验,它们在实际代码中被积极使用,尤其是在宏中。因此,您不能将表达式参数限制为特定的类型联合 - 表达式可以包含任何值。
当然,在设计新语言时,您可以对其施加任何限制。也许,限制Expr
为仅包含Symbol
,Expr
和 some Literal
s 在某些情况下会很有用。但这违背了 Julia 和 Clojure 的简单性和灵活性原则,并且会显着降低宏的有用性。