是否可以使用静态类型的完整Lisp变体?存在这样的东西甚至有意义吗?我相信Lisp语言的优点之一就是其定义的简单性。静态类型会损害这一核心原则吗?
是否可以使用静态类型的完整Lisp变体?存在这样的东西甚至有意义吗?我相信Lisp语言的优点之一就是其定义的简单性。静态类型会损害这一核心原则吗?
Answers:
是的,这是很有可能的,尽管对于大多数惯用的Lisp / Scheme代码而言,标准的HM样式类型系统通常是错误的选择。有关使用静态类型的“ Full Lisp”(实际上更类似于Scheme)的最新语言,请参见Typed Racket。
Sexpr
。
coerce :: a->b
评估的方式来写。类型安全在哪里?
eval
您需要测试结果以查看结果,这在Typed Racked中没有什么新的(与采用String
和类型的并集类型的函数相同Number
)。可以做到这一点的一种隐含方式是,您可以用HM静态类型的语言编写和使用动态类型的语言。
如果您想要的只是一种看起来像 Lisp 的静态类型的语言,则可以通过定义代表您的语言的抽象语法树,然后将该AST映射到S表达式来轻松实现。但是,我认为我不会将结果称为Lisp。
如果您希望除了语法之外还具有Lisp-y特性的东西,可以使用静态类型的语言来实现。但是,Lisp有许多特性很难获得很多有用的静态类型。为了说明这一点,让我们看一下称为cons的列表结构本身,它构成了Lisp的主要构建块。
将cons称为清单,虽然(1 2 3)
看起来像一个清单,但有点用词不当。例如,它根本无法与静态类型列表(例如C ++ std::list
或Haskell列表)相比。这些是一维链接列表,其中所有单元格均为同一类型。Lisp高兴地允许(1 "abc" #\d 'foo)
。另外,即使您将静态类型的列表扩展为涵盖列表列表,这些对象的类型也要求列表中的每个元素都是一个子列表。您将如何代表((1 2) 3 4)
他们?
Lisp cons形成二叉树,具有叶子(原子)和分支(cones)。此外,这种树的叶子可能根本不包含任何原子(非cons)Lisp类型!这种结构的灵活性使Lisp非常擅长处理符号计算,AST和Lisp代码本身的转换!
那么如何用静态类型的语言对这种结构进行建模呢?让我们在Haskell中尝试一下,它具有强大而精确的静态类型系统:
type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons
| CAtom Atom
您的第一个问题将是Atom类型的范围。显然,我们还没有选择一种足够灵活的Atom类型来涵盖我们想悬而未决的所有类型的对象。假设我们有一个神奇的类型类,而不是尝试扩展上面列出的Atom数据结构(您可以清楚地看到它很脆弱)。Atomic
,区分了所有我们想要构成原子的类型。然后,我们可以尝试:
class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons
| CAtom a
但这是行不通的,因为它要求树中的所有原子都属于同一类型。我们希望它们能够因叶而异。更好的方法需要使用Haskell的存在量词:
class Atomic a where ?????
data Cons = CCons Cons Cons
| forall a. Atomic a => CAtom a
但是现在您到了问题的症结所在。您可以用这种结构的原子做什么?它们可以用Atomic a
什么共同点建模?这样的类型可以保证什么类型的安全性?请注意,我们没有在类型类中添加任何函数,这是有充分理由的:原子在Lisp中没有任何共同点。它们在Lisp中的超类型简称为t
(即top)。
为了使用它们,您必须想出一种机制来动态地将原子的值强制为您可以实际使用的值。至此,您基本上已经在静态类型语言中实现了一个动态类型子系统!(人们不禁会注意到Greenspun的《第十条编程规则》的一个可能推论。)
请注意,Haskell仅为这种具有类型的动态子系统提供支持,该子系统与类型和Typeable类Obj
结合使用以替换我们的类,该类允许将任意值与其类型存储在一起,并从这些类型中显式强制返回。这就是您需要使用Lisp cons结构完全通用的系统。Dynamic
Atomic
您还可以执行另一种方法,将静态类型的子系统嵌入本质上为动态类型的语言中。这使您可以对程序的各个部分进行静态类型检查,从而可以利用更严格的类型要求。例如,这似乎是CMUCL的有限类型的精确类型检查中采用的方法。
最后,可能存在两个单独的子系统,它们是动态和静态类型的,它们使用合同样式的编程来帮助导航这两个子系统之间的过渡。这样,该语言可以适应Lisp的用法,在这种情况下,静态类型检查将比帮助更多地成为障碍,以及在静态类型检查将是有利的情况下使用。这是Typed Racket所采用的方法,您将在随后的评论中看到。
(Listof Integer)
和(Listof Any)
。显然,您可能会怀疑后者是无用的,因为您对类型一无所知,但是在TR中,您可以稍后使用它(if (integer? x) ...)
,并且系统会知道它x
是第一个分支中的Integer。
dynamic
类型已在静态语言中流行,这是一种获得动态类型语言好处的解决方法,这些值的通常折衷以使类型可识别的方式包装。但是在这里,类型化的球拍在使语言更方便方面也做得非常好-类型检查器使用谓词的出现来更多地了解类型。例如,请参见球拍页面上的键入示例,并查看如何string?
将字符串和数字列表“简化”为字符串列表。
我的回答可能没有高度的信心。例如,如果您查看SML之类的语言并将其与Lisp进行比较,则每种语言的功能核心几乎都是相同的。结果,在Lisp的核心(函数应用程序和原始值)上应用某种静态类型似乎并不麻烦。
您的问题的确可以说是完整的,而我看到的其中一些问题是“代码即数据”方法。类型比表达式存在的层次更抽象。Lisp没有这种区别-一切在结构上都是“平坦的”。如果我们考虑某个表达式E:T(其中T是其类型的某种表示形式),然后我们将该表达式视为纯数据,那么T的类型到底是什么?好吧,这是一种!一种是较高的订单类型,所以让我们继续在代码中讲一些有关此的内容:
E : T :: K
您可能会看到我要去的地方。我敢肯定,通过从代码中分离出类型信息,可以避免这种类型的自引用,但是这会使类型的味道不那么“轻浮”。解决这个问题的方法可能有很多,尽管对我来说,最好的方法并不明显。
编辑:哦,所以经过一番谷歌搜索,我发现了Qi,它看起来与Lisp非常相似,只是它是静态类型的。也许这是开始查看他们进行更改以在其中进行静态键入的好地方。