/ / Pourquoi ne puis-je pas faire correspondre une famille de caractères? - haskell, types

Pourquoi ne puis-je pas faire correspondre une famille de caractères? - haskell, types

Considérez le code suivant:

{-# LANGUAGE TypeFamilies #-}

data Twothings a b = Twothings a b

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing a
Leftthing a = a

leftthing :: a -> Leftthing a
leftthing (Twothings a b) = leftthing a
leftthing b = b

Il ne compile pas, avec l'erreur suivante:

Couldn"t match expected type ‘a’
with actual type ‘Twothings a0 t0’
‘a’ is a rigid type variable bound by
the type signature for:
leftthing :: forall a. a -> Leftthing a

Il se plaint de la ligne leftthing (Twothings a b) = leftthing a. Si je comprends bien, il ne peut "pas unifier la variable de type a dans la signature de type avec le type du constructeur Twothings. Ok, cela semble avoir du sens. Mais alors, Comment puis-je jamais définir une fonction avec des familles de types dans la signature de type?

Réponses:

11 pour la réponse № 1

Lorsque vous déclarez

leftthing :: a -> Leftthing a

vous dites que le votre interlocuteur de leftthing arrive à choisir quoi a est.

Quand vous écrivez alors

leftthing (Twothings a b) = leftthing a

tu es présumer qu'ils ont choisi un Twothings tapez, et comme ce n'est pas nécessairement le cas, votre programme est rejeté.

Vous avez peut-être pensé que vous étiez tester si ils avaient choisi un Twothings tapez, mais non! Les informations de type sont effacées avant l'exécution, il n'y a donc aucun moyen de faire un tel test.

Toi pouvez essayez de restaurer les informations d'exécution nécessaires. Permettez-moi d'abord de réparer l'incohérence entre votre Leftthing et leftthing.

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing{-you forgot the recursion!-} a
Leftthing a = a

Maintenant, nous pouvons définir le GADT de témoins à Twothingness.

data IsItTwothings :: * -> * where
YesItIs   :: IsItTwothings a -> IsItTwothings (Twothings a b)
NoItIsn"t :: Leftthing a ~ a => IsItTwothings a
-- ^^^^^^^^^^^^^^^ this constraint will hold for any type
-- which is *definitely not* a Twothings type

Et ensuite, nous pouvons passer le témoin en argument:

leftthing :: IsItTwothings a -> a -> Leftthing a
leftthing (YesItIs r) (Twothings a b) = leftthing r a
leftthing NoItIsn"t   b               = b

En effet, le témoin est l’encodage unaire du nombre de Twothingses à la racine de votre type. C’est assez d’informations pour déterminer au moment de l’exécution la quantité correcte de décompression à effectuer.

> leftthing (YesItIs (YesItIs NoItIsn"t)) (Twothings (Twothings True 11) (Twothings "strange" [42]))
True

Pour résumer, vous ne pouvez "pas trouver un type par motifcorrespondant à une valeur. Vous devez plutôt connaître le type de correspondance de modèle (car le type détermine la disposition de la mémoire et il n'y a pas de balises de type à l'exécution). Vous ne pouvez pas "taper directement sur les types (car ils ne sont tout simplement pas mis en correspondance). Vous pouvez construire des types de données qui agissent comme des preuves d'exécution de la structure de type et correspondent à celles-ci.

Peut-être qu'un jour, votre programme fonctionnera si vous lui donnez le type

leftthing :: pi a. a -> Leftthing a

pi est le quantificateur dépendant, indiquant que l'argument de type masqué n'est pas effacé, mais plutôt transmis et mis en correspondance au moment de l'exécution. Ce jour n'est pas encore venu, mais je pense que ce sera le cas.