Розв’язання типових отворів


105

Нещодавно я з’ясував, що типові отвори в поєднанні зі збіркою візерунків на доказках забезпечують досить хороший досвід, схожий на Agda в Haskell. Наприклад:

{-# LANGUAGE
    DataKinds, PolyKinds, TypeFamilies, 
    UndecidableInstances, GADTs, TypeOperators #-}

data (==) :: k -> k -> * where
    Refl :: x == x

sym :: a == b -> b == a
sym Refl = Refl 

data Nat = Zero | Succ Nat

data SNat :: Nat -> * where
    SZero :: SNat Zero
    SSucc :: SNat n -> SNat (Succ n)

type family a + b where
    Zero   + b = b
    Succ a + b = Succ (a + b)

addAssoc :: SNat a -> SNat b -> SNat c -> (a + (b + c)) == ((a + b) + c)
addAssoc SZero b c = Refl
addAssoc (SSucc a) b c = case addAssoc a b c of Refl -> Refl

addComm :: SNat a -> SNat b -> (a + b) == (b + a)
addComm SZero SZero = Refl
addComm (SSucc a) SZero = case addComm a SZero of Refl -> Refl
addComm SZero (SSucc b) = case addComm SZero b of Refl -> Refl
addComm sa@(SSucc a) sb@(SSucc b) =
    case addComm a sb of
        Refl -> case addComm b sa of
            Refl -> case addComm a b of
                Refl -> Refl 

Справді приємна річ у тому, що я можу замінити праву частину Refl -> expконструкцій типовим отвором, а цільові типи моїх отворів оновлюються доказом, майже так само, як у rewriteформі в Agda.

Однак іноді отвір просто не оновлюється:

(+.) :: SNat a -> SNat b -> SNat (a + b)
SZero   +. b = b
SSucc a +. b = SSucc (a +. b)
infixl 5 +.

type family a * b where
    Zero   * b = Zero
    Succ a * b = b + (a * b)

(*.) :: SNat a -> SNat b -> SNat (a * b)
SZero   *. b = SZero
SSucc a *. b = b +. (a *. b)
infixl 6 *.

mulDistL :: SNat a -> SNat b -> SNat c -> (a * (b + c)) == ((a * b) + (a * c))
mulDistL SZero b c = Refl
mulDistL (SSucc a) b c = 
    case sym $ addAssoc b (a *. b) (c +. a *. c) of
        -- At this point the target type is
        -- ((b + c) + (n * (b + c))) == (b + ((n * b) + (c + (n * c))))
        -- The next step would be to update the RHS of the equivalence:
        Refl -> case addAssoc (a *. b) c (a *. c) of
            Refl -> _ -- but the type of this hole remains unchanged...

Крім того, незважаючи на те, що цільові типи не обов'язково вирівнюються всередині доказів, якщо я вставляю цілу річ від Agda, вона все одно перевіряє:

mulDistL' :: SNat a -> SNat b -> SNat c -> (a * (b + c)) == ((a * b) + (a * c))
mulDistL' SZero b c = Refl
mulDistL' (SSucc a) b c = case
    (sym $ addAssoc b (a *. b) (c +. a *. c),
    addAssoc (a *. b) c (a *. c),
    addComm (a *. b) c,
    sym $ addAssoc c (a *. b) (a *. c),
    addAssoc b c (a *. b +. a *. c),
    mulDistL' a b c
    ) of (Refl, Refl, Refl, Refl, Refl, Refl) -> Refl

Чи є у вас ідеї, чому це відбувається (або як я міг зробити надійне переписування доказів)?


8
Хіба ти не чекаєш трохи? Узгодження зразка на доказ рівності встановлює (двонаправлену) рівність. Зовсім не зрозуміло, де і в якому напрямку ви хочете, щоб це застосовано до цільового типу. Наприклад, ви можете пропустити symдзвінки, mulDistL'і ваш код все одно перевірятиметься.
kosmikus

1
Цілком можливо, я очікую занадто багато. Однак у багатьох випадках це працює так само, як і в Агді, тому було б корисно з’ясувати закономірності поведінки. Я не оптимістичний, оскільки справа, ймовірно, глибоко пов'язана з надрами типової перевірки.
András Kovács

1
У вашому питанні це трохи ортогонально, але ви можете зняти ці докази, скориставшись набором рівняльних комбінаторів міркувань à la Agda. Ср. це доказ концепції
gallais

Відповіді:


1

Якщо ви хочете генерувати всі можливі подібні значення, тоді ви можете написати функцію для цього або за допомогою наданих або заданих меж.

Можливо, цілком можливо використовувати типи церковних чисел на рівні або щось подібне, щоб примусити їх створювати, але це майже напевно занадто багато роботи для того, що ви, мабуть, хочете / потребуєте.

Це може бути не тим, що ви хочете (тобто "За винятком використання just (x, y), оскільки z = 5 - x - y"), але це має більше сенсу, ніж намагатися мати якесь примусове обмеження на рівні типу, щоб дозволити дійсне значення.


-3

Це відбувається тому, що значення визначаються під час виконання. Це може призвести до перетворення значень залежно від того, що введено під час виконання, отже, передбачається, що дірка вже оновлена.


3
Звичайно значення визначаються під час виконання, але це не має нічого спільного з питанням. Необхідна інформація вже доступна у відповідності шаблонів на GADT.
int_index
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.