Éviter la pollution des espaces de noms dans Haskell

J’utilise beaucoup d’enregistrements différents dans un programme, certains d’entre eux utilisant les mêmes noms de champs, par exemple

data Customer = Customer { ..., foo :: Int, ... } data Product = Product { ..., foo :: Int, ... } 

Maintenant que la fonction d’accesseur “foo” est définie deux fois, j’obtiens l’erreur “Multiple declarations”. Une façon d’éviter cela consisterait à utiliser différents modules importés entièrement qualifiés, ou simplement à renommer les champs (ce que je ne veux pas faire).

Quelle est la manière officielle de traiter cela dans Haskell?

C’est un problème très poilu. Il existe plusieurs propositions pour corriger le système d’enregistrement. Sur une note connexe, voir TDNR et discussion sur le café .

En utilisant les fonctionnalités de langage actuellement disponibles, je pense que la meilleure option consiste à définir les deux types dans deux modules différents et à effectuer une importation qualifiée. De plus, si vous le souhaitez, vous pouvez implémenter des machines de type classe.

Dans Customer.hs

 module Customer where data Customer = Customer { ..., foo :: Int, ... } 

Dans Product.hs

 module Product where data Product = Product { ..., foo :: Int, ... } 

En les utilisant, en Third.hs

 module Third where import qualified Customer as C import qualified Product as P .. C.foo .. .. P.foo .. 

Pourtant, j’imagine que ce ne sera pas trop tard avant que vous renconsortingez le problème des modules dépendant récursivement .

(FYI, cette question est presque certainement un double)

Solutions:

1) Préfixez les champs avec une étiquette indiquant le type (extrêmement commun)

 data Customer = Customer {..., cFoo :: Int, ...} 

2) Utiliser des classes de type (moins courantes, les gens se plaignent que les préfixes comme cFoo sont pas pratiques mais évidemment pas si cFoo qu’ils écrivent une classe et une instance ou utilisent TH pour faire la même chose).

 class getFoo a where foo :: a -> Int instance getFoo Customer where foo = cFoo 

3) Utiliser de meilleurs noms de champs Si les champs sont réellement différents (ce qui n’est pas toujours le cas, mon ordinateur a un âge similaire à celui de mon employé), alors c’est la meilleure solution.

Voir aussi le paquet Has: http://chrisdone.com/posts/duck-typing-in-haskell

Et si vous avez vraiment besoin d’ enregistrements extensibles maintenant, vous pouvez toujours utiliser HList. Mais je ne recommanderais pas cela tant que vous n’êtes pas vraiment familier et à l’aise avec le Haskell moyennement avancé, et même alors je vérifierais trois fois que vous en avez besoin.

Haskelldb a une version légèrement plus légère: http://hackage.haskell.org/packages/archive/haskelldb/2.1.0/doc/html/Database-HaskellDB-HDBRec.html

Et puis il y a une autre version des enregistrements extensibles dans la bibliothèque de pamplemousses frp: http://hackage.haskell.org/package/grapefruit-records

Encore une fois, pour vos besoins, je mordrais la balle et renommerais simplement les champs. Mais ces références montrent que lorsque vous avez vraiment besoin de la puissance des enregistrements extensibles, il existe des moyens d’y parvenir, même si aucune n’est aussi agréable qu’une extension de langage bien conçue.

Il existe une extension de langage DuplicateRecordFields qui permet la duplication des fonctions de champ et en déduit le type par des annotations de type.

Voici un petit exemple (script haskell-stack ):

 #!/usr/bin/env stack -- stack runghc --resolver lts-8.20 --install-ghc {-# LANGUAGE DuplicateRecordFields #-} newtype Foo = Foo { baz :: Ssortingng } newtype Bar = Bar { baz :: Ssortingng } foo = Foo { baz = "foo text" } bar = Bar { baz = "bar text" } main = do putStrLn $ "Foo: " ++ baz (foo :: Foo) -- Foo: foo text putStrLn $ "Bar: " ++ baz (bar :: Bar) -- Bar: bar text