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?
bs
und cs
und 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 Int
In von cs
kommt bs
(und as
leer ist). Es ist auch möglich, dass es as
stattdessen von (nicht leer) kommt und dass Int
es später wieder auftaucht cs
. Wir müssen tiefer eintauchen, um cs
zu wissen, und GHC wird das nicht tun.
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?