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))
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))' –
Devrait être possible. Essayez-le! –