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 Concatableich 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?
bsund csund wir wollen den Fundep ausnutzen, dh wir wollen rekonstruieren as. Um dies deterministisch zu tun, erwarten wir, dass wir uns auf eine einzelne Instanz festlegen und dieses Rezept befolgen können. Konkret annehmen bs = (Int ': bs2)und cs = (Int ': cs2). Welche Instanz wählen wir? Es ist möglich, dass ein solches IntIn von cskommt bs(und asleer ist). Es ist auch möglich, dass es asstattdessen von (nicht leer) kommt und dass Intes später wieder auftaucht cs. Wir müssen tiefer eintauchen, um cszu wissen, und GHC wird das nicht tun.
bs cs -> as, weil wir nicht-lokale Informationen benötigenbsundcsentscheiden müssen, obases 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?