Résolution irrégulière du type de trou

105

J'ai récemment découvert que les trous de type combinés à la correspondance de motifs sur les épreuves offrent une expérience assez agréable de type Agda dans Haskell. Par exemple:

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

Ce qui est vraiment sympa, c'est que je peux remplacer les côtés droits des Refl -> expconstructions par un type de trou, et mes types de cible de trou sont mis à jour avec la preuve, à peu près comme avec le rewriteformulaire dans Agda.

Cependant, parfois, le trou ne parvient pas à se mettre à jour:

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

De plus, même si les types de cibles ne s'alignent pas nécessairement à l'intérieur de la preuve, si je colle le tout depuis Agda, cela vérifie toujours bien:

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

Avez-vous une idée de pourquoi cela se produit (ou comment je pourrais réécrire des preuves de manière robuste)?

András Kovács
la source
8
Vous ne vous attendez pas à grand chose? La correspondance de modèle sur une preuve d'égalité établit une égalité (bidirectionnelle). On ne sait pas du tout où et dans quelle direction vous voudriez qu'il soit appliqué au type de cible. Par exemple, vous pouvez omettre les symappels mulDistL'et votre code sera toujours vérifié.
kosmikus
1
J'attends probablement trop. Cependant, dans de nombreux cas, cela fonctionne comme dans Agda, il serait donc toujours utile de déterminer les régularités du comportement. Je ne suis pas optimiste cependant, car la question est probablement profondément impliquée dans les entrailles du vérificateur de type.
András Kovács
1
C'est un peu orthogonal à votre question, mais vous pouvez tirer ces preuves en utilisant un ensemble de combinateurs de raisonnement équationnel à la Agda. Cf. cette preuve de concept
gallais

Réponses:

1

Si vous souhaitez générer toutes ces valeurs possibles, vous pouvez écrire une fonction pour le faire, avec des limites fournies ou spécifiées.

Il peut très bien être possible d'utiliser des numéros d'église au niveau du type ou d'autres afin d'imposer la création de ceux-ci, mais c'est presque certainement trop de travail pour ce que vous voulez / avez probablement besoin.

Ce n'est peut-être pas ce que vous voulez (c'est-à-dire "Sauf d'utiliser juste (x, y) puisque z = 5 - x - y") mais cela a plus de sens que d'essayer d'avoir une sorte de restriction appliquée au niveau du type pour autoriser des valeurs.

Billykart
la source
-3

Cela se produit parce que les valeurs sont déterminées au moment de l'exécution. Il peut entraîner une transformation des valeurs en fonction de ce qui est entré au moment de l'exécution, par conséquent, il suppose que le trou est déjà mis à jour.

ajay
la source
3
Bien sûr, les valeurs sont déterminées lors de l'exécution, mais cela n'a rien à voir avec la question. Les informations nécessaires sont déjà disponibles à partir de la correspondance de motifs sur un GADT.
int_index