Типовая ракетка очень отличается от Хаскелла. Системы типов в 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 будет оборачивать элементы в Left
false и в них 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.