Un buen hecho verdadero sobre la concatenación es que si conozco dos variables en la ecuación:
a ++ b = c
Entonces sé el tercero.
Me gustaría capturar esta idea en mi propia concat, así que uso una dependencia funcional.
{-# 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
Ahora conjuro una lista heterogénea así:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Pero cuando trato de declarar esto ya Concatable
que tengo 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)
No satisfago mi tercera dependencia funcional. O más bien, el compilador cree que nosotros no. Esto se debe a que el compilador cree que en nuestra segunda instancia podría ser el caso bs ~ (a ': cs)
. Y podría ser el caso si Concatable as (a ': cs) cs
.
¿Cómo puedo ajustar mis instancias para que se satisfagan las tres dependencias?
bs
y cs
queremos explotar el fundep, es decir, queremos reconstruir as
. Para hacerlo de una manera determinista, esperamos poder comprometernos con una sola instancia y seguir esa receta. Concretamente, asumir bs = (Int ': bs2)
y cs = (Int ': cs2)
. ¿Qué instancia elegimos? Es posible que tal Int
en cs
proviene de bs
(y as
está vacía). También es posible que provenga de (no vacío) as
y que Int
vuelva a aparecer más cs
adelante. Necesitamos profundizar cs
para saber y GHC no lo hará.
bs cs -> as
, porque necesitamos información no local sobrebs
ycs
decidir sias
debería ser una desventaja o no. Necesitamos descubrir cómo representar esta información; ¿Qué contexto agregaríamos a una firma de tipo para garantizarla cuando no se puede deducir directamente?