Возможен ли статический тип полного варианта Lisp? - PullRequest
101 голосов
/ 24 июля 2010

Возможен ли статический тип полного варианта Lisp?Имеет ли смысл даже что-то подобное существовать?Я считаю, что одним из достоинств языка Lisp является простота его определения.Будет ли статическая типизация нарушать этот основной принцип?

Ответы [ 4 ]

53 голосов
/ 24 июля 2010

Да, это очень возможно, хотя стандартная система типов в стиле HM обычно является неправильным выбором для большинства идиоматического кода на Лиспе / Схеме. См. Typed Racket , чтобы узнать о недавнем языке "Full Lisp" (на самом деле больше похожем на Scheme) со статической типизацией.

31 голосов
/ 24 июля 2010

Если вам нужен только статически типизированный язык, который выглядит как Lisp, вы можете сделать это довольно легко, определив абстрактное синтаксическое дерево, представляющее ваш язык, и затем отобразив этот AST в S-выражениях. Тем не менее, я не думаю, что назвал бы результат Lisp.

Если вам нужно что-то, что на самом деле имеет характеристики Lisp-y, кроме синтаксиса, это можно сделать с помощью статически типизированного языка. Однако в Lisp есть много характеристик, из которых трудно получить много полезной статической типизации. Чтобы проиллюстрировать это, давайте взглянем на саму структуру списка, называемую cons , которая образует основной строительный блок Lisp.

Называть минусы списком, хотя (1 2 3) выглядит как единое целое, немного ошибочно. Например, он совсем не похож на статически типизированный список, такой как C ++ std::list или список Хаскелла. Это одномерные связанные списки, в которых все ячейки относятся к одному типу. Лисп с радостью разрешает (1 "abc" #\d 'foo). Кроме того, даже если вы расширяете свои статические списки для охвата списков списков, тип этих объектов требует, чтобы каждый элемент списка был подсписком. Как бы вы представили ((1 2) 3 4) в них?

Лисп-конусы образуют бинарное дерево с листьями (атомами) и ветвями (консусами). Кроме того, листья такого дерева могут вообще содержать любой атомарный (не минус) тип 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? Какой уровень безопасности типов вам гарантирован для такого типа? Обратите внимание, что мы не добавили никаких функций в наш класс типов, и есть веская причина: атомы не имеют ничего общего в Лиспе. Их супертип в Лиспе просто называется t (то есть top).

Чтобы использовать их, вам нужно было бы придумать механизмы для динамического приведения значения атома к чему-то, что вы действительно можете использовать. И в этот момент вы в основном реализовали динамически типизированную подсистему в своем статически типизированном языке! (Нельзя не отметить возможное следствие Десятого правила программирования Гринспуна .)

Обратите внимание, что Haskell обеспечивает поддержку именно такой динамической подсистемы с типом Obj, используемой совместно с типом Dynamic и классом Typeable для замены нашей Atomic класс, позволяющий хранить произвольные значения вместе с их типами, и явное приведение обратно к этим типам. Именно такую ​​систему вам нужно использовать для работы со структурами Lisp-конов в их полной общности.

Что вы также можете сделать, это пойти другим путем и встроить статически типизированную подсистему в по существу динамически типизированный язык.Это позволяет вам использовать статическую проверку типов для частей вашей программы, которые могут использовать более строгие требования к типу.Это, кажется, подход, принятый в ограниченной форме CMUCL, например, точной проверки типов .

Наконец, есть возможность иметь две отдельные подсистемы, динамически и статически типизированные, которые используют контрактстиль программирования, чтобы помочь перемещаться между переходами.Таким образом, язык может приспосабливаться к использованию Lisp, где статическая проверка типов была бы скорее помехой, чем помощью, а также к случаям, когда статическая проверка типов была бы выгодна.Это подход, принятый Typed Racket , как вы увидите из последующих комментариев.

10 голосов
/ 24 июля 2010

Мой ответ без высокой степени уверенности - , вероятно .Если вы посмотрите на такой язык, как, например, SML, и сравните его с Лиспом, функциональное ядро ​​каждого из них практически идентично.В результате, похоже, у вас не возникнет особых проблем с применением какой-либо статической типизации к ядру Lisp (приложения-функции и значения примитивов).

Ваш вопрос говорит: full хотя и где я вижу, что проблема заключается в подходе «код как данные».Типы существуют на более абстрактном уровне, чем выражения.У Лиспа нет этого различия - все «плоское» по структуре.Если мы рассмотрим какое-либо выражение E: T (где T - некоторое представление своего типа), а затем мы рассмотрим это выражение как обычные данные, тогда что именно представляет собой тип T здесь?Ну, это так!Вид - это более высокий тип заказа, поэтому давайте просто скажем что-нибудь об этом в нашем коде:

E : T :: K

Вы можете увидеть, куда я иду с этим.Я уверен, что, отделяя информацию о типе от кода, можно было бы избежать такого рода самоссылки типов, однако это сделало бы типы не очень «лишними» по своему вкусу.Вероятно, есть много способов обойти это, хотя для меня не очевидно, какой из них был бы лучшим.

РЕДАКТИРОВАТЬ: О, так что, немного погуглив, я нашел Qi , который появляетсябыть очень похожим на Лисп, за исключением того, что он статически типизирован.Возможно, это хорошее место, чтобы начать видеть, где они внесли изменения, чтобы получить статическую типизацию.

...