Eine nette wahre Tatsache über die Verkettung ist, dass, wenn ich zwei Variablen in der Gleichung kenne:
a ++ b = c
Dann kenne ich den dritten.
Ich möchte diese Idee in meinem eigenen Concat festhalten, damit ich eine funktionale Abhängigkeit verwende.
{-# Language DataKinds, GADTs, FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, PolyKinds, TypeOperators, UndecidableInstances #-}
import Data.Kind (Type)
class Concatable
(m :: k -> Type)
(as :: k)
(bs :: k)
(cs :: k)
| as bs -> cs
, as cs -> bs
, bs cs -> as
where
concat' :: m as -> m bs -> m cs
Jetzt zaubere ich eine heterogene Liste wie folgt:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Aber wenn ich versuche, diese zu deklarieren, habe Concatable
ich ein Problem
instance Concatable HList '[] bs bs where
concat' HEmpty bs = bs
instance
( Concatable HList as bs cs
)
=> Concatable HList (a ': as) bs (a ': cs)
where
concat' (HCons head tail) bs = HCons head (concat' tail bs)
Ich erfülle meine dritte funktionale Abhängigkeit nicht. Oder besser gesagt, der Compiler glaubt, dass wir das nicht tun. Dies liegt daran, dass der Compiler der Ansicht ist, dass dies in unserer zweiten Instanz der Fall sein könnte bs ~ (a ': cs)
. Und es könnte der Fall sein, wenn Concatable as (a ': cs) cs
.
Wie kann ich meine Instanzen so anpassen, dass alle drei Abhängigkeiten erfüllt sind?
quelle
bs cs -> as
, weil wir nicht-lokale Informationen benötigenbs
undcs
entscheiden müssen, obas
es ein Nachteil oder eine Null sein soll. Wir müssen herausfinden, wie diese Informationen dargestellt werden können. Welchen Kontext würden wir einer Typensignatur hinzufügen, um sie zu garantieren, wenn sie nicht direkt abgeleitet werden kann?bs
undcs
und wir wollen den Fundep ausnutzen, dh wir wollen rekonstruierenas
. Um dies deterministisch zu tun, erwarten wir, dass wir uns auf eine einzelne Instanz festlegen und dieses Rezept befolgen können. Konkret annehmenbs = (Int ': bs2)
undcs = (Int ': cs2)
. Welche Instanz wählen wir? Es ist möglich, dass ein solchesInt
In voncs
kommtbs
(undas
leer ist). Es ist auch möglich, dass esas
stattdessen von (nicht leer) kommt und dassInt
es später wieder auftauchtcs
. Wir müssen tiefer eintauchen, umcs
zu wissen, und GHC wird das nicht tun.Antworten:
Wie die Kommentare vermuten lassen, wird GHC es nicht alleine herausfinden, aber wir können es mit ein bisschen Programmierung auf Typebene unterstützen. Lassen Sie uns einige vorstellen
TypeFamilies
. Alle diese Funktionen sind ziemlich einfache Übersetzungen von Listenmanipulationen, die auf die Textebene angehoben wurden:Mit diesen Tools können wir tatsächlich das Stundenziel erreichen, aber zuerst definieren wir eine Funktion mit den gewünschten Eigenschaften:
cs
ausas
und abzuleitenbs
as
ausbs
und abzuleitencs
bs
ausas
und abzuleitencs
Voila:
Testen wir es:
Und schließlich das Endziel:
quelle