Un bel fatto vero sulla concatenazione è che se conosco due variabili nell'equazione:
a ++ b = c
Allora conosco il terzo.
Vorrei catturare questa idea nel mio concat così uso una dipendenza funzionale.
{-# 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
Ora evoco un elenco eterogeneo in questo modo:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Ma quando provo a dichiararli perché Concatable
ho un problema
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)
Non soddisfo la mia terza dipendenza funzionale. O meglio, il compilatore ritiene che non lo facciamo. Questo perché il compilatore ritiene che nella nostra seconda istanza potrebbe essere così bs ~ (a ': cs)
. E potrebbe essere il caso se Concatable as (a ': cs) cs
.
Come posso adattare le mie istanze in modo che tutte e tre le dipendenze siano soddisfatte?
bs
e cs
, e vogliamo sfruttare il fondo, cioè vogliamo ricostruire as
. Per farlo in modo deterministico, ci aspettiamo di essere in grado di impegnarci in una singola istanza e seguire quella ricetta. Concretamente, assumere bs = (Int ': bs2)
e cs = (Int ': cs2)
. Quale istanza scegliamo? È possibile che tale Int
nel cs
proviene bs
(ed as
è vuoto). È anche possibile che provenga da (non vuoto) as
e che Int
apparirà di nuovo in cs
seguito. Dobbiamo scavare più a fondo cs
per sapere e GHC non lo farà.
bs cs -> as
, perché abbiamo bisogno di informazioni non locali subs
ecs
per decidere seas
dovrebbe essere un contro o uno zero. Dobbiamo scoprire come rappresentare queste informazioni; quale contesto aggiungeremmo a una firma di tipo per garantirlo quando non può essere dedotto direttamente?