Risoluzione del tipo di foro irregolare


105

Recentemente ho scoperto che i buchi di tipo combinati con la corrispondenza del modello sulle dimostrazioni forniscono un'esperienza simile a quella di Agda in Haskell. Per esempio:

{-# 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 

La cosa veramente bella è che posso sostituire il lato destro delle Refl -> expcostruzioni con un tipo di buco, e i miei tipi di buco di destinazione vengono aggiornati con la dimostrazione, più o meno come con la rewriteforma in Agda.

Tuttavia, a volte il buco non si aggiorna:

(+.) :: 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...

Inoltre, anche se i tipi di destinazione non si allineano necessariamente all'interno della prova, se incollo l'intera cosa da Agda continua a controllare bene:

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

Hai qualche idea sul perché questo accada (o su come potrei riscrivere le prove in modo efficace)?


8
Non ti aspetti un po 'troppo? Il pattern matching su una prova di uguaglianza sta stabilendo un'uguaglianza (bidirezionale). Non è affatto chiaro dove e in quale direzione vorresti che fosse applicato al tipo di target. Ad esempio, potresti omettere le symchiamate mulDistL'e il tuo codice verrebbe comunque controllato.
kosmikus

1
Molto probabilmente mi aspetto troppo. Tuttavia, in molti casi funziona proprio come in Agda quindi sarebbe comunque utile capire le regolarità del comportamento. Non sono ottimista, però, dal momento che la questione è probabilmente profondamente coinvolta nelle viscere del controllore di tipo.
András Kovács

1
È un po 'ortogonale alla tua domanda, ma puoi tirare fuori queste dimostrazioni usando una serie di combinatori di ragionamento equazionale à la Agda. Cf. questa prova di concetto
gallais

Risposte:


1

Se si desidera generare tutti i possibili valori di questo tipo, è possibile scrivere una funzione per farlo, con i limiti forniti o specificati.

Potrebbe benissimo essere possibile usare i numeri della chiesa a livello di tipo o altri in modo da imporre la creazione di questi, ma è quasi sicuramente troppo lavoro per ciò che probabilmente vuoi / hai bisogno.

Questo potrebbe non essere quello che vuoi (es. "Tranne che usare solo (x, y) poiché z = 5 - x - y") ma ha più senso che cercare di avere qualche tipo di restrizione forzata a livello di tipo per consentire valori.


-3

Succede perché i valori sono determinati in fase di esecuzione. Può provocare una trasformazione dei valori a seconda di ciò che viene inserito in fase di esecuzione, quindi si presume che il foro sia già aggiornato.


3
Ovviamente i valori vengono determinati in fase di esecuzione, ma non ha nulla a che fare con la domanda. Le informazioni necessarie sono già disponibili dal pattern-matching su un GADT.
int_index
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.