Supposons que j'ai deux types de données Foo et Bar. Foo a des champs x et y. La barre a les champs x et z. Je veux être en mesure d'écrire une fonction qui prend un Foo ou un Bar comme paramètre, extrait la valeur x, effectue un calcul dessus, puis retourne un nouveau Foo ou Bar avec la valeur x définie en conséquence.Syntaxe d'enregistrement Haskell et classes de type
Voici une approche:
class HasX a where
getX :: a -> Int
setX :: a -> Int -> a
data Foo = Foo Int Int deriving Show
instance HasX Foo where
getX (Foo x _) = x
setX (Foo _ y) val = Foo val y
getY (Foo _ z) = z
setY (Foo x _) val = Foo x val
data Bar = Bar Int Int deriving Show
instance HasX Bar where
getX (Bar x _) = x
setX (Bar _ z) val = Bar val z
getZ (Bar _ z) = z
setZ (Bar x _) val = Bar x val
modifyX :: (HasX a) => a -> a
modifyX hasX = setX hasX $ getX hasX + 5
Le problème est que tous ces accesseurs sont douloureuses à écrire, surtout si je remplace Foo et Bar avec des types de données réelles qui ont beaucoup de domaines.
La syntaxe d'enregistrement de Haskell donne une manière beaucoup plus agréable de définir ces enregistrements. Mais, si je tente de définir les dossiers comme celui-ci
data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show
Je reçois une erreur disant que x est défini plusieurs fois. Et, je ne vois aucun moyen de faire partie d'une classe de type afin que je puisse les passer à modifyX.
Existe-t-il une bonne manière propre de résoudre ce problème, ou est-ce que je suis bloqué avec définir mes propres getters et setters? En d'autres termes, existe-t-il un moyen de connecter les fonctions créées par la syntaxe d'enregistrement avec les classes de types (les getters et les setters)?
EDIT
est ici le vrai problème, je suis en train de résoudre. J'écris une série de programmes liés qui utilisent tous System.Console.GetOpt pour analyser leurs options de ligne de commande. Il y aura beaucoup d'options de ligne de commande communes à tous ces programmes, mais certains programmes peuvent avoir des options supplémentaires. Je voudrais que chaque programme soit capable de définir un enregistrement contenant toutes ses valeurs d'option. Je commence alors avec une valeur d'enregistrement par défaut qui est ensuite transformée par une monade StateT et GetOpt pour obtenir un enregistrement final reflétant les arguments de ligne de commande. Pour un seul programme, cette approche fonctionne très bien, mais j'essaie de trouver un moyen de réutiliser le code dans tous les programmes.
Si vous aviez un seul type de données 'data FooBar = Foo {x :: Int, y :: Int} | Bar {x :: Int, z :: Int} 'vous n'auriez pas ce problème. Si vos types de données se trouvaient dans des modules différents, vous pourriez utiliser '{- # LANGUAGE DisambiguateRecordFields # -}'. Des raisons de coller avec votre design actuel? – ephemient