Типы в Лиспе и Схеме

10

Теперь я вижу, что у Ракетки есть типы. На первый взгляд кажется, что он почти идентичен печатанию на Haskell. Но CLOS Лиспа покрывает некоторые из типов пространства Хаскеля? Создание очень строгого типа Haskell и объекта на любом языке OO выглядит примерно одинаково. Просто я выпил немного хул-хелскера на Haskell и совершенно параноидален, что если я пойду по дороге на Лиспе, меня обидят из-за динамического набора текста.

user2054900
источник

Ответы:

6

Система типов CL более выразительна, чем система Haskell, например, вы можете иметь тип (or (integer 1 10) (integer 20 30))для значения 1,2,...9,10,20,21,...,30.

Однако компиляторы Lisp не навязывают им понимание безопасности типов, поэтому вы можете игнорировать их «заметки» - на свой страх и риск .

Это означает, что вы можете написать Haskell на Лиспе (так сказать), объявив все типы значений и тщательно убедившись, что все необходимые типы выведены, но тогда проще использовать Haskell в первую очередь.

В основном, если вы хотите строгую статическую типизацию, используйте Haskell или OCaml, если вы хотите строгую динамическую типизацию, используйте Lisp. Если вы хотите слабую статическую типизацию, используйте C, если вы хотите слабую динамическую типизацию, используйте Perl / Python. У каждого пути есть свои преимущества (и фанатики) и недостатки (и хулители), поэтому вы извлечете пользу из изучения всех их.

ДСН
источник
19
Любой, использующий такие термины, как «принудительная защита типов в горле», не понимает, что такое безопасность типов или почему она полезна.
Мейсон Уилер
11
@MasonWheeler: любой, кто делает быстрый вывод из одной фразы, чаще ошибается, чем иначе. Как, например, в этом случае.
sds
4
Поскольку тема «языки», термин «у тебя в горле» уместен и подходит для образов.
luser droog
1
@KChaloux: я имел в виду «выразительный», как показано в примере.
SDS
4
У тебя все это задом наперед. Динамическая типизация - это особый случай статической типизации, когда вы заставляете программиста использовать 1 тип для всего. Я могу сделать то же самое (многословно) в большинстве языков со статической типизацией, объявив каждую переменную как тип Objectили каков бы ни был корень дерева типов. Это менее выразительны, потому что вы лишены от варианта сказать , что некоторые переменные могут содержать только определенные значения.
Довал
5

Типовая ракетка очень отличается от Хаскелла. Системы типов в Lisp и Scheme, и в действительности системы типов в традиционно нетипизированных языковых экосистемах, имеют фундаментальную цель, которой не достигают другие системы типов - взаимодействие с существующим нетипизированным кодом . Например, Typed Racket ввел совершенно новые правила набора текста для соответствия различным идиомам Racket. Рассмотрим эту функцию:

(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

Для непустых списков возвращается первый элемент. Для пустых списков это возвращает false. Это часто встречается в нетипизированных языках; типизированный язык будет использовать какой-либо тип оболочки, например, Maybeили выдавать ошибку в пустом регистре. Если мы хотим добавить тип к этой функции, какой тип следует использовать? Это не [a] -> a(в нотации Haskell), потому что он может вернуть false. Это также не так [a] -> Either a Boolean, потому что (1) он всегда возвращает false в пустом регистре, а не в произвольном логическом значении, и (2) тип Either будет оборачивать элементы в Leftfalse и в них Rightи требовать, чтобы вы «развернули оба», чтобы добраться до фактического элемента. Вместо этого значение возвращает истинное объединение- нет конструкторов обтекания, он просто возвращает один тип в некоторых случаях и другой тип в других случаях. В Typed Racket это представлено конструктором типа объединения:

(: first (All (A) (-> (Listof A) (U A #f))))
(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

Тип (U A #f)сообщает, что функция может вернуть либо элемент списка, либо false без какого-либо Eitherэкземпляра переноса . Средство проверки типов может выводить, что оно some-listимеет тип (Pair A (Listof A))или пустой список, и, кроме того, оно делает вывод, что в двух ветвях оператора if известно, какой из них имеет место . Средство проверки типов знает, что в (car some-list)выражении список должен иметь тип, (Pair A (Listof A))потому что условие if обеспечивает это. Это называется типизацией вхождений и предназначено для облегчения перехода от нетипизированного кода к типизированному коду.

Проблема в миграции. Существует масса нетипизированного кода Racket, и Typed Racket не может просто заставить вас отказаться от всех ваших любимых нетипизированных библиотек и потратить месяц на добавление типов в базу кода, если вы хотите его использовать. Эта проблема применяется всякий раз, когда вы добавляете типы постепенно в существующую кодовую базу, см. TypeScript и его тип Any для применения этих идей в javascript.

Система постепенного типа должна предоставлять инструменты для работы с общими нетипизированными идиомами и взаимодействия с существующим нетипизированным кодом. В противном случае его использование будет весьма болезненным, см. «Почему мы больше не используем Core.typed» для примера Clojure.

Джек
источник
1
В экономике это известно как закон Грешама : плохие деньги вытесняют хорошие. Это имеет тенденцию находить приложения в разработке тоже. Когда у вас есть две теоретически эквивалентные подсистемы в вашей системе, слишком часто худшая вызывает слишком много проблем, чтобы сделать более выгодную, как мы видим здесь.
Мейсон Уилер
«Пример разработки системы типов, которая»: это предложение не полно
coredump
@MasonWheeler Хуже, позвольте мне угадать, динамическая проверка типов. Так что, как только вы представите его, не стоит проводить статический анализ?
coredump
1
@coredump - постепенная типизация того стоит, если только вы плохо взаимодействуете с нетипизированным кодом. Например, тип Any в TypeScript просто говорит о том, что проверяющий тип должен отказаться, в основном отбрасывая преимущества системы типов, потому что это может привести к распространению неверного значения в больших количествах статически проверенного кода. Typed Racket использует контракты и границы модулей, чтобы избежать этой проблемы.
Джек,
1
@coredump Прочитайте статью Clojure. Наличие динамической типизации, особенно со всем сторонним кодом, у которого не было статических типов, действительно испортило их попытки улучшить свою кодовую базу статическими типами.
Мейсон Уилер