Ich versuche, die Assoziativität von Listen auf Typebene so zu beweisen, dass ich zwischen äquivalenten Typen konvertieren kann, ohne Einschränkungen mitzunehmen.
Angenommen, die Standarddefinition der Verkettung:
type family (++) (xs :: [k]) (ys :: [k]) :: [k] where
'[] ++ ys = ys
(x ': xs) ++ ys = x ': (xs ++ ys)
Angenommen, ich habe eine Funktion:
given :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy ((a ++ b) ++ c)
given = Proxy -- Proxy is just an example
und ich möchte diese Funktion aufrufen und dann Assoziativität verwenden:
my :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy (a ++ (b ++ c))
my = given @k @a @b @c -- Couldn't match type ‘(a ++ b) ++ c’ with ‘a ++ (b ++ c)’
Diese Typgleichheit ist in der Tat nicht trivial, daher ist es keine Überraschung, dass der Compiler sie nicht versteht, aber ich kann es beweisen! Leider weiß ich nicht, wie ich den Compiler davon überzeugen kann, dass ich es kann.
Mein natürlicher erster Gedanke ist, etwas zu tun wie:
proof :: forall k (a :: [k]) (b :: [k]) (c :: [k]). (a ++ (b ++ c)) :~: ((a ++ b) ++ c)
proof = _
und dann ändere meine Funktion in:
my :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy (a ++ (b ++ c))
my = case proof @k @a @b @c of Refl -> given @k @a @b @c
Aber ich muss noch definieren proof
und dafür muss ich eine Induktion für seine Typargumente durchführen. Die einzige Möglichkeit, Induktionen für Typen in Haskell durchzuführen, die ich kenne, besteht darin, eine Typklasse zu definieren, aber dann muss ich dem Typ von die entsprechende Einschränkung hinzufügen my
, die ich nicht tun möchte - die Tatsache, dass sie aufgerufen wird given
und erzwingt, dass das Ergebnis ein „Implementierungsdetail“ ist.
Gibt es eine Möglichkeit, diese Art der Typengleichheit in Haskell zu beweisen, ohne auf unsichere Postulate zurückzugreifen?
(a++(b++c)) :~: ((a++b)++c)
ohne weitere Singleton-Argumente oder Typklasseneinschränkungen schreiben können .