Un beau fait vrai sur la concaténation est que si je connais deux variables dans l'équation:
a ++ b = c
Alors je connais le troisième.
Je voudrais capturer cette idée dans mon propre concat donc j'utilise une dépendance fonctionnelle.
{-# 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
Maintenant, j'évoque une liste hétérogène comme ceci:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Mais quand j'essaye de les déclarer car Concatable
j'ai un problème
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)
Je ne satisfait pas ma troisième dépendance fonctionnelle. Ou plutôt le compilateur pense que non. C'est parce que le compilateur pense que dans notre deuxième cas, cela pourrait être le cas bs ~ (a ': cs)
. Et cela pourrait être le cas si Concatable as (a ': cs) cs
.
Comment puis-je ajuster mes instances afin que les trois dépendances soient satisfaites?
haskell
typeclass
functional-dependencies
type-level-computation
Sriotchilism O'Zaic
la source
la source
bs cs -> as
, car nous avons besoin d'informations non locales surbs
etcs
pour décider si celaas
devrait être un contre ou un zéro. Nous devons découvrir comment représenter ces informations; quel contexte ajouter à une signature de type pour la garantir lorsqu'elle ne peut pas être directement déduite?bs
etcs
, et nous voulons exploiter le fundep, c'est-à-dire que nous voulons reconstruireas
. Pour le faire de manière déterministe, nous nous attendons à pouvoir nous engager sur une seule instance et suivre cette recette. Concrètement, supposezbs = (Int ': bs2)
etcs = (Int ': cs2)
. Quelle instance choisissons-nous? Il est possible qu'une telleInt
entréecs
vienne debs
(etas
soit vide). Il est également possible que cela vienne de (non vide) à laas
place, et celaInt
réapparaîtra pluscs
tard. Nous devons approfondircs
pour savoir et GHC ne le fera pas.Réponses:
Donc, comme le suggèrent les commentaires, GHC ne va pas le comprendre par lui-même, mais nous pouvons l'aider avec un peu de programmation au niveau du type. Présentons-en
TypeFamilies
. Toutes ces fonctions sont des traductions assez simples de la manipulation de liste élevée au niveau du type:Avec ces outils à notre disposition, nous pouvons réellement atteindre l'objectif de l'heure, mais définissons d'abord une fonction avec les propriétés souhaitées:
cs
deas
etbs
as
debs
etcs
bs
deas
etcs
Voila:
Testons-le:
Et enfin l'objectif final:
la source