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 Concatableque 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?
haskell
typeclass
functional-dependencies
type-level-computation
Sriotchilism O'Zaic
fuente
fuente

bs cs -> as, porque necesitamos información no local sobrebsycsdecidir siasdeberí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?bsycsqueremos explotar el fundep, es decir, queremos reconstruiras. Para hacerlo de una manera determinista, esperamos poder comprometernos con una sola instancia y seguir esa receta. Concretamente, asumirbs = (Int ': bs2)ycs = (Int ': cs2). ¿Qué instancia elegimos? Es posible que talIntencsproviene debs(yasestá vacía). También es posible que provenga de (no vacío)asy queIntvuelva a aparecer máscsadelante. Necesitamos profundizarcspara saber y GHC no lo hará.Respuestas:
Entonces, como sugieren los comentarios, GHC no lo resolverá por sí solo, pero podemos ayudarlo con un poco de programación a nivel de tipo. Vamos a presentar algunos
TypeFamilies. Todas estas funciones son traducciones bastante sencillas de manipulación de listas elevadas al nivel de tipo:Con estas herramientas a nuestra disposición, podemos llegar al objetivo por hora, pero primero definamos una función con las propiedades deseadas:
csa partir deasybsasa partir debsycsbsa partir deasycsVoila
Probémoslo:
Y finalmente el objetivo final:
fuente