0

J'ai un type de données qui porte un type 'caché' (inféré) et une valeur concrète. Maintenant j'essaye d'implémenter une fonction qui change les deux mais je n'arrive pas à la faire passer GHC.Transformer un type paramétré (avec un paramètre inféré)

Mon exemple de code est le suivant:

data T tag val = T val 

data A = A 
data B = B 

mkIntVal :: T a b -> T Int b 
mkIntVal (T x) = T x 

mkCharVal :: T a b -> T Char b 
mkCharVal (T x) = T x 

convert :: T Int a -> T Char b 
convert (T A) = mkCharVal $ T B 
convert (T B) = mkCharVal $ T A 

L'erreur qu'il produit est ceci:

test.hs:13:12: 
    Couldn't match type `A' with `B' 
    In the pattern: A 
    In the pattern: T A 
    In an equation for `convert': convert (T A) = mkCharVal $ T B 

test.hs:13:17: 
    Couldn't match type `B' with `A' 
    Expected type: T Char b 
     Actual type: T Char B 
    In the expression: mkCharVal $ T B 
    In an equation for `convert': convert (T A) = mkCharVal $ T B 

Qu'est-ce qui doit être fait pour faire ce travail? Dois-je changer la structure de données?


EDIT

Je suis en train d'étendre la solution de Don Stewart travailler avec des types de données polymorphes. Je joue autour de la définition de l'instance, mais le plus prometteur à la recherche d'un est venu avec ceci:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FlexibleContexts #-} 

data C a = C a deriving Show 

class Convertable inVal outVal outTag | outVal -> outTag where 
    convert :: T Int inVal -> T outTag outVal 

instance Convertable A B Char where 
    convert (T A) = mkCharVal $ T B 

instance Convertable B A Char where 
    convert (T B) = mkCharVal $ T A 

instance Convertable a b Char => Convertable (C a) (C (T Char b)) Char where 
    convert (T (C val)) = mkCharVal $ T (C (convert val)) -- line 29 

Mais cela me donne juste un autre message d'erreur:

test.hs:29:57: 
    Could not deduce (a ~ T Int inVal0) 
    from the context (Convertable a b Char) 
     bound by the instance declaration at test.hs:28:10-70 
     `a' is a rigid type variable bound by 
      the instance declaration at test.hs:28:22 
    In the first argument of `convert', namely `val' 
    In the first argument of `C', namely `(convert val)' 
    In the first argument of `T', namely `(C (convert val))' 

Comme Don dit qu'il devrait être possible, je suis intéressé par la façon dont cela serait mis en œuvre.


Solution

Après beaucoup plus « jouer » Je suis finalement arrivé avec quelque chose qui fonctionne. Cela vous semble-t-il bon?

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 


data T tag val = T val deriving Show 

data A = A deriving Show 
data B = B deriving Show 
data C a = C a deriving Show 


class Convertable inTag inVal outTag outVal | inTag -> outTag, inVal -> outVal 
where 
    convert :: T inTag inVal -> T outTag outVal 

instance Convertable Int A Char B where 
    convert (T A) = T B 

instance Convertable Int B Char A where 
    convert (T B) = T A 

instance (Convertable Int (T Int a) Char (T Char b), Convertable Int a Char b) 
    => Convertable Int (C (T Int a)) Char (C (T Char b)) where 
    convert (T (C x)) = T (C (convert x)) 

instance Convertable Int (C (T Int A)) Char (C (T Char B)) where 
    convert (T (C x)) = T (C (convert x)) 

instance Convertable Int (C (T Int B)) Char (C (T Char A)) where 
    convert (T (C x)) = T (C (convert x)) 

Utilisation:

*Main> convert $ mkIntVal $ T $ C $ mkIntVal $ T A 
T (C (T B)) 
*Main> :t it 
it :: T Char (C (T Char B)) 

Répondre

1

Chaque cas de votre fonction convert a un autre type en conflit:

convertA :: T t A -> T Char B 
convertA (T A) = mkCharVal $ T B 

convertB :: T t B -> T Char A 
convertB (T B) = mkCharVal $ T A 

Vous pouvez unifier ces via une classe de types,

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 


class C a b c | b -> c where 
    convert :: T t a -> T c b 

instance C A B Char where 
    convert (T A) = mkCharVal (T B) 

instance C B A Char where 
    convert (T B) = mkCharVal (T A) 

si vous Je souhaite une seule fonction qui, à différents types, se convertit dans des directions différentes. Notez comment cela prend T avec un tag, le supprime et remplace le tag et la valeur par un nouveau tag déterminé par le type de valeur.

+0

Maintenant, je comprends ce qui n'allait pas avec ma tentative. Serait-il possible ultérieurement d'étendre cette solution aux types de résultats polymorphes ou est-elle restreinte aux types «normaux»? Comme je n'aurais aucune valeur concrète à mettre dans la définition d'instance je ne sais pas comment j'irais pour une fonction comme 'convertir (T (C val)) = mkCharVal $ T (C (convertir val))' –

+0

Devrait être possible. Essayez-le! –

Questions connexes