是否可以使用静态类型的完整Lisp变体?

Lam*_*ate 101 lisp programming-languages static-typing

是否可以使用静态类型的完整Lisp变体?这样的事情存在甚至是否有意义?我相信Lisp语言的优点之一就是它的定义简单.静态类型会破坏这个核心原则吗?

Eli*_*lay 55

是的,这是非常可能的,尽管标准的HM风格类型系统通常是大多数惯用的Lisp/Scheme代码的错误选择.请参阅Typed Racket,了解最近使用静态类型的"Full Lisp"语言(实际上更像是Scheme).

  • 那通常是"Sexpr". (18认同)
  • 这里的问题是,构成类型化球拍程序的整个源代码的列表是什么类型? (2认同)
  • @ssice:当你使用像'eval`这样的无类型函数时,你需要测试结果才能看出结果是什么,这在Typed Racked中并不是什么新东西(同一个交易作为一个带有'String`联合类型的函数) `Number`).一种看待这个*可以*完成的隐含方式是你可以*在HM静态类型语言中编写和使用动态类型语言. (2认同)

Owe*_* S. 33

如果你想要的只是一个看起来像 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 conses形成一个二叉树,有叶子(原子)和分支(conses).此外,这种树的叶子可能包含任何原子(非缺点)Lisp类型!这种结构的灵活性使得Lisp在处理符号计算,AST和转换Lisp代码本身时非常擅长!

那么你如何用静态类型语言模拟这样的结构呢?让我们在Haskell中尝试它,它有一个非常强大和精确的静态类型系统:

type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons 
            | CAtom Atom
Run Code Online (Sandbox Code Playgroud)

您的第一个问题将是Atom类型的范围.很明显,我们还没有选择具有足够灵活性的Atom类型来覆盖我们想要在锥形中徘徊的所有类型的对象.而不是尝试扩展上面列出的Atom数据结构(你可以清楚地看到它是脆弱的),让我们说我们有一个神奇的类型类Atomic,它区分了我们想要制作原子的所有类型.然后我们可以尝试:

class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons 
                          | CAtom a
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为它要求树中的所有原子属于同一类型.我们希望它们能够从叶子到叶子不同.更好的方法需要使用Haskell的存在量词:

class Atomic a where ?????
data Cons = CCons Cons Cons 
            | forall a. Atomic a => CAtom a 
Run Code Online (Sandbox Code Playgroud)

但现在你来到了问题的关键.在这种结构中你能用原子做什么?他们有什么共同的结构可以建模Atomic a?这种类型可以保证什么级别的类型安全?注意我们没有向我们的类类添加任何函数,并且有一个很好的理由:原子在Lisp中没有共同点.他们在Lisp中的超类型被简单地称为t(即顶部).

为了使用它们,你必须提出动态强制将原子的值强制转换为你可以实际使用的东西的机制.此时,您基本上已经在静态类型语言中实现了动态类型的子系统!(人们不禁注意到格林斯普的第十个规划规则的可能必然结果.)

请注意,Haskell支持这样一个带有类型的动态子系统,它Obj与一个Dynamic类型和一个Typeable类一起使用来替换我们的Atomic类,允许任意值与它们的类型一起存储,并从这些类型中显式强制.这就是您需要使用的系统,以完全普遍地使用Lisp cons结构.

你还可以做的另一种方法是,在一个基本上动态类型的语言中嵌入一个静态类型的子系统.这使您可以利用静态类型检查程序中可以利用更严格类型要求的部分.例如,这似乎是CMUCL有限形式的精确类型检查所采用的方法.

最后,有两个独立的子系统,动态和静态类型,使用契约式编程来帮助导航两者之间的过渡.这样,语言可以适应Lisp的用法,其中静态类型检查比帮助更多地是障碍,以及静态类型检查将是有利的用途.这是Typed Racket采用的方法,您将从后面的评论中看到.

  • 这个答案有一个根本问题:你假设静态类型系统*必须是HM风格.那里无法表达的基本概念是Lisp代码的一个重要特性,它是一种子类型.如果你看一下打字的球拍,你会发现它可以很容易地表达任何类型的列表 - 包括`(Listof Integer)`和`(Listof Any)`.显然,你怀疑后者是没用的,因为你对类型一无所知,但在TR中,你以后可以使用`(if(integer?x)...)`并且系统会知道`x`是第一个分支中的整数. (15认同)
  • 哦,这是对打字球拍的不良表征(这与你在某些地方发现的不健全的类型系统不同).Typed Racket*是*a*静态类型*语言,对于类型化代码没有运行时开销.Racket仍然允许在TR中编写一些代码,而某些代码使用通常的无类型语言 - 对于这些情况,合同(动态检查)用于保护类型代码,防止可能行为不端的无类型代码. (5认同)

Gia*_*ian 10

我的答案很可能没有高度的信心.例如,如果你看一下像SML这样的语言,并将它与Lisp进行比较,那么每个语言的功能核心几乎完全相同.因此,在Lisp的核心(函数应用程序和原始值)中应用某种静态类型似乎不会有太多麻烦.

你的问题确实说得很完整,而且我发现其中的一些问题是代码为数据的方法.类型存在于比表达式更抽象的级别.Lisp没有这种区别 - 结构中的一切都是"平坦的".如果我们考虑一些表达式E:T(其中T是其类型的某种表示),然后我们将此表达式视为普通的''数据,那么这里的T类型到底是什么?嗯,这是一种!一种是更高级的订单类型,所以让我们继续在我们的代码中说一些:

E : T :: K
Run Code Online (Sandbox Code Playgroud)

你可能会看到我要去的地方.我确信通过从代码中分离出类型信息,可以避免这种类型的自我引用,但这会使类型不是非常"口齿不清".可能有很多方法可以解决这个问题,但对我来说这并不是最明显的方法.

编辑:哦,所以有点谷歌搜索,我发现Qi,它似乎非常类似于Lisp,除了它是静态类型.也许这是一个很好的开始,看看他们在哪里进行更改以获得静态输入.


Rai*_*wig 6

Dylan:扩展 Dylan 的类型系统以实现更好的类型推断和错误检测

  • @BjörnLindqvist:如果我们通过渐进类型添加静态类型,并且在编译期间检查这些类型,那么这就是静态类型。这并不是整个程序都是静态类型的,而是部分/区域是静态类型的。http://homes.sice.indiana.edu/jsiek/what-is-gradual-typing/ '渐进式打字是我在 2006 年与 Walid Taha 一起开发的一种类型系统,它允许程序的某些部分动态打字,而其他部分则可以动态打字。是静态类型的。 (2认同)