Маленький наборщик: Как запретить аргументы с более конкретным типом?

#types #racket #pie-lang

Вопрос:

Я хочу понять и правильно использовать технику более конкретных типов, чтобы исключить нежелательные аргументы. Пример, приведенный около 6.37, представляет собой функцию first , которая обертывает head то, что нельзя вызвать для пустых векторов. Их решение состоит в том, чтобы смещать объявление типа с 1:

 (claim first
    (Pi ((E U) (l Nat))
        (-> (Vec E (add1 l))
            E)))
 

Это неудобно, потому что, если первые два параметра являются типом вектора и длиной, вы должны «лгать» при его вызове:

 (first Atom 2 (vec:: 'A (vec:: 'B (vec:: 'C vecnil))))
 

ВОПРОС № 1: Я неправильно это осмысливаю? Необходимость «лгать», чтобы обойти что-то, кажется, что что-то на самом деле не понято.

first Я сказал, что отправляю вектор длины атома 2, когда на самом деле он имеет длину 3. Чтобы назвать гипотетическим second , потребуется нечестность на 2. Для third , нечестность на 3 и так далее.

Я подумал, не лучше ли думать о l не как о длине вектора, а как о версии функции. Нет такой версии first , которая принимала бы пустой вектор. Его нулевая версия принимает векторную длину 1. Его вторая версия имеет векторную длину 2:

первая версия первый тип
0 (-> (Vec E 1) E)
1 (-> (Vec E 2) E)

ВОПРОС № 2: Правильно ли так думать об этом? В книге используется l , поэтому, похоже, они хотят, чтобы читатели думали «о длине».

Имея это в виду, я решил скорректировать first , чтобы выбрать тип входного вектора и его длину. Старая реализация теперь first-worker включена и дополнена новой first :

 (claim first-worker
    (Pi ((T U) (version Nat))
        (-> (Vec T (add1 version)) ; the i'th version takes vectors length i 1
            T)))

(define first-worker
    (lambda (E l)
        (lambda (es)
            (head es))))

(claim first
    (Pi ((E U) (length Nat))
        (-> (Vec E length) E)))

(define first
    (lambda (E length input)
        (first-worker E (sub1 length) input))) ; length vectors call version length-1

(first-worker Atom 3 (vec:: 'A (vec:: 'B (vec:: 'C vecnil))))
 

Но теперь нет защиты от первого с пустым вектором! Вызов типа (first Atom 0 vecnil) все еще разрешен, если бы он выполнял проверку типа (это не так).

ВОПРОС № 3: Как я могу написать a first , который принимает истинную длину входного вектора?

Любые другие советы и исправления приветствуются.