Возможен ли статически типизированный полный вариант Лиспа?

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


person Lambda the Penultimate    schedule 24.07.2010    source источник
comment
Мне нравятся макросы произвольной формы Lisp, но мне нравится надежность системы типов Haskell. Мне бы хотелось посмотреть, как выглядит статически типизированный Лисп.   -  person mcandre    schedule 13.07.2011
comment
Хороший вопрос! Я считаю, что shenlanguage.org это делает. Я бы хотел, чтобы это стало более популярным.   -  person Hamish Grubijan    schedule 10.02.2013
comment
github.com/carp-lang/Carp   -  person Anentropic    schedule 09.11.2018
comment
Как вы выполняете символические вычисления с помощью Haskell? (решить 'x' (= (+ x y) (* x y))). Если вы поместите его в строку, проверки не будет (в отличие от Lisp, который может использовать макросы для добавления проверки). Если вы используете алгебраические типы данных или списки ... Это будет очень многословно: решить (Sym x) (Eq (Plus (Sym x) (Sym y)) (Mult (Sym x) (Sym y)))   -  person aoeu256    schedule 04.08.2019


Ответы (4)


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

person Eli Barzilay    schedule 24.07.2010
comment
Проблема здесь в том, что это за тип списка, который составляет весь исходный код типизированной программы для рэкетов? - person Zorf; 26.07.2010
comment
Обычно это Sexpr. - person Eli Barzilay; 27.07.2010
comment
Но тогда я могу написать coerce :: a->b в терминах eval. Где безопасность типов? - person ssice; 18.05.2016
comment
@ssice: когда вы используете нетипизированную функцию, такую ​​как eval, вам нужно проверить результат, чтобы увидеть, что получится, что не является новым в Typed Racked (то же самое, что и функция, которая принимает тип объединения String и Number). Неявный способ увидеть, что это можно сделать, - это тот факт, что вы можете написать и использовать язык с динамической типизацией на языке со статической типизацией HM. - person Eli Barzilay; 19.05.2016
comment
Почему стандартный стиль HM обычно не подходит для лиспа? - person Danielhu; 30.01.2021

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

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

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

Конусы Лиспа образуют двоичное дерево с листьями (атомами) и ветвями (конусами). Кроме того, листья такого дерева могут содержать любой атомарный (не являющийся минусом) тип Лиспа! Гибкость этой структуры делает 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 (т.е. верхний).

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

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

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

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

person Owen S.    schedule 24.07.2010
comment
Этот ответ страдает фундаментальной проблемой: вы предполагаете, что системы статических типов должны быть в стиле HM. Основная концепция, которая не может быть выражена здесь и является важной особенностью кода Lisp, - это создание подтипов. Если вы посмотрите на набранную ракетку, вы увидите, что она может легко отображать любой список, включая такие вещи, как (Listof Integer) и (Listof Any). Очевидно, вы подозреваете, что последнее бесполезно, потому что вы ничего не знаете о типе, но в TR вы можете позже использовать (if (integer? x) ...), и система будет знать, что x является целым числом в 1-й ветви. - person Eli Barzilay; 25.07.2010
comment
Да, и это плохая характеристика типизированной ракетки (которая отличается от ненадежных систем типов, которые вы можете найти в некоторых местах). Typed Racket - это статически типизированный язык, без дополнительных затрат времени выполнения для типизированного кода. Racket по-прежнему позволяет писать часть кода на TR, а часть на обычном нетипизированном языке - и в этих случаях контракты (динамические проверки) используются для защиты типизированного кода от потенциально некорректного нетипизированного кода. - person Eli Barzilay; 25.07.2010
comment
@Eli Barzilay: Спасибо, и у меня есть ответ из трех частей: 1. Я думаю, что этот аргумент применим и к другим статически типизированным системам, я просто выбрал систему типов HM для иллюстрации. Как вы заметили, вам нужно сделать (Listof Any) для поддержки произвольного дерева cons-кода Лиспа, верно? Я хочу сказать, что чем больше вы пытаетесь создать структуру для работы с Lisp-y, тем более универсальным и бесполезным будет тип, который вам придется кормить системой типов. - person Owen S.; 25.07.2010
comment
@Eli Barzilay: 2. integer? можно кодировать, если вы работаете с Typeable объектами: case (cast a :: Int) of Just _ -> True; Nothing -> False. Так что да, Haskell поддерживает этот ограниченный вид подтипов. Обычно я использую классы типов в Haskell для того, чтобы делать то, что я мог бы использовать для создания подтипов в C ++ или Lisp, и если бы мне пришлось попробовать Lisp с использованием системы типов HM, я бы хотел сохранить этот подход. Я думаю, это было бы работоспособно. Но я не пробовал. :-) - person Owen S.; 25.07.2010
comment
@Eli Barzilay: 3. Вы правы, я не понимал, что вы можете перейти от Typed Racket к Racket. Я считаю Typed Racket и нетипизированную Racket частями единого целого, что, я уверен, и было вашим намерением. :-) Попробую уточнить. - person Owen S.; 25.07.2010
comment
@Eli Barzilay: Я солгал, здесь четыре части: 4. Мне интересно, как принятый в отрасли стиль кодирования C ++ постепенно уходит от подтипов к обобщениям. Слабость заключается в том, что язык не предоставляет помощи для объявления интерфейса, который будет использовать универсальная функция, в чем, безусловно, могут помочь классы типов. Кроме того, C ++ 0x может добавлять вывод типа. Не HM, я полагаю, но ползет в том направлении? - person Owen S.; 25.07.2010
comment
Оуэн: (1) суть в том, что вам нужны подтипы для выражения того, что пишут лисперы кода - а этого просто не может быть с системами HM, поэтому вы вынуждены использовать настраиваемые типы и конструкторы для каждого использования, что значительно усложняет использование всего этого. В типизированном рэкете использование системы с подтипами было следствием намеренного дизайнерского решения: результат должен иметь возможность выражать типы такого кода без изменения кода или создания пользовательских типов. - person Eli Barzilay; 25.07.2010
comment
(2) Да, dynamic типы становятся популярными в статических языках как своего рода обходной путь для получения некоторых преимуществ динамически типизированных языков с обычным компромиссом, заключающимся в том, что эти значения обертываются таким образом, чтобы сделать типы идентифицируемыми. Но и здесь типизированный рэкет очень хорошо справляется с задачей сделать его удобным в рамках языка - средство проверки типов использует вхождения предикатов, чтобы узнать больше о типах. Например, просмотрите типизированный пример на странице рэкетов и посмотрите, как string? сокращает список строк и чисел до список строк. - person Eli Barzilay; 25.07.2010
comment
(3) Если вы рассматриваете их как часть единого целого, то, очевидно, вы бы увидели в этом способ ввода частей кода. Но на практике очень возможно (и часто это делается на практике) использовать только типизированный язык, поэтому для всех практических целей вы работаете в статически типизированной среде. (Что касается (4), я не знаю, как движется C ++, но если там больше функций, которые сложно использовать, я уверен, что они туда доберутся ...) - person Eli Barzilay; 25.07.2010

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

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

E : T :: K

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

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

person Gian    schedule 24.07.2010
comment
Похоже, что следующая итерация после Qi - это Shen, разработанный тем же человеком. - person Diagon; 23.08.2019

Дилан: Расширение системы типов Дилана для лучшего вывода типов и обнаружение ошибок

person Rainer Joswig    schedule 24.07.2010
comment
Ссылка мертвая. Но в любом случае Дилан не имеет статической типизации. - person Björn Lindqvist; 15.04.2019
comment
@ BjörnLindqvist: эта ссылка была на тезис о добавлении постепенного набора текста для Дилана. - person Rainer Joswig; 15.04.2019
comment
@ BjörnLindqvist: Я дал ссылку на обзорную статью. - person Rainer Joswig; 15.04.2019
comment
Но постепенный набор текста не считается статическим. Если бы это было так, то Pypy был бы статически типизированным Python, поскольку он также использует постепенную типизацию. - person Björn Lindqvist; 15.04.2019
comment
@ BjörnLindqvist: если мы добавляем статические типы с помощью постепенной типизации и они проверяются во время компиляции, то это статическая типизация. Просто не вся программа статически типизирована, а части / регионы. homes.sice.indiana.edu/jsiek/what-is-gradual -typing «Постепенная типизация - это система типов, которую я разработал вместе с Валидом Тахой в 2006 году, которая позволяет динамически типизировать части программы, а другие части - статически». - person Rainer Joswig; 15.04.2019