Fehlerhafte Lochtypauflösung

105

Ich habe kürzlich herausgefunden, dass Typlöcher in Kombination mit Mustervergleich auf Proofs eine ziemlich schöne Agda-ähnliche Erfahrung in Haskell bieten. Beispielsweise:

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

Das wirklich Schöne ist, dass ich die rechten Seiten der Refl -> expKonstruktionen durch ein Typloch ersetzen kann und meine Lochzieltypen mit dem Beweis aktualisiert werden, so ziemlich wie mit dem rewriteFormular in Agda.

Manchmal kann das Loch jedoch einfach nicht aktualisiert werden:

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

Auch wenn die Zieltypen nicht unbedingt im Proof ausgerichtet sind, wird beim Einfügen des Ganzen von Agda immer noch einwandfrei überprüft:

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

Haben Sie eine Idee, warum dies passiert (oder wie ich das Umschreiben von Proofs auf robuste Weise durchführen könnte)?

András Kovács
quelle
8
Erwarten Sie nicht ein bisschen viel? Der Mustervergleich auf einem Gleichheitsnachweis stellt eine (bidirektionale) Gleichheit her. Es ist überhaupt nicht klar, wo und in welche Richtung es auf den Zieltyp angewendet werden soll. Zum Beispiel könnten Sie die symAnrufe weglassen mulDistL'und Ihr Code würde immer noch prüfen.
Kosmikus
1
Möglicherweise erwarte ich zu viel. In vielen Fällen funktioniert es jedoch genauso wie in Agda, sodass es immer noch nützlich wäre, die Regelmäßigkeiten des Verhaltens herauszufinden. Ich bin jedoch nicht optimistisch, da die Angelegenheit wahrscheinlich stark mit dem Darm des Typprüfers zusammenhängt.
András Kovács
1
Es ist ein bisschen orthogonal zu Ihrer Frage, aber Sie können diese Beweise mit einer Reihe von Kombinatoren für gleiches Denken à la Agda ziehen. Vgl. dieser Proof of Concept
Gallais

Antworten:

1

Wenn Sie alle möglichen derartigen Werte generieren möchten, können Sie dazu eine Funktion schreiben, entweder mit bereitgestellten oder angegebenen Grenzen.

Es mag durchaus möglich sein, Kirchennummern auf Typebene oder ähnliche zu verwenden, um die Erstellung dieser zu erzwingen, aber es ist fast definitiv zu viel Arbeit für das, was Sie wahrscheinlich wollen / brauchen.

Dies ist möglicherweise nicht das, was Sie möchten (dh "Außer die Verwendung von nur (x, y), da z = 5 - x - y"), aber es ist sinnvoller, als zu versuchen, eine erzwungene Einschränkung der Typebene zuzulassen, um die Gültigkeit zuzulassen Werte.

Billykart
quelle
-3

Dies geschieht, weil die Werte zur Laufzeit ermittelt werden. Es kann eine Transformation von Werten bewirken, abhängig davon, was zur Laufzeit eingegeben wird. Daher wird davon ausgegangen, dass das Loch bereits aktualisiert wurde.

ajay
quelle
3
Natürlich werden die Werte zur Laufzeit ermittelt, aber das hat nichts mit der Frage zu tun. Die erforderlichen Informationen sind bereits beim Pattern Matching auf einem GADT verfügbar.
int_index