Question

Excusez-moi pour mon Haskell-fu extrêmement limité.

J'ai une série de types de data, définis dans différents modules, qui sont structurés de la même façon:

-- in module Foo
data Foo = Foo [Param]

-- in module Bar
data Bar = Bar [Param]

-- * many more elsewhere

Je voudrais avoir un ensemble de fonctions qui fonctionnent sur la liste des params, par exemple pour ajouter et supprimer des éléments de la liste (retourner une nouvelle Foo ou Bar avec une autre liste de params, selon le cas).

Pour autant que je peux dire, même si je crée une classe de types et créer des instances pour chaque type, je dois définir toutes ces fonctions à chaque fois, à savoir:

-- in some imported module

class Parameterized a where
    addParam :: a -> Param -> a
    -- ... other functions

-- in module Foo

instance Parameterization Foo where
    addParam (Foo params) param = Foo (param:params)
    -- ... other functions

-- in module Bar

instance Parameterization Bar where
    -- this looks familiar...
    addParam (Bar params) param = Bar (param:params)
    -- ... other functions

Cela se sent fastidieux - bien au-delà du degré où je commence à penser que je fais quelque chose de mal. Si vous ne pouvez pas correspondance de motif quel que soit le constructeur (?) Pour extraire une valeur, comment peut-partout comme ceci réduit?

Pour réfuter une ligne possible d'argument: oui, je sais que je pourrais simplement avoir un ensemble de fonctions (addParam, etc.), qui dresserait une liste explicitement chaque match constructeur et le modèle mis les params - mais comme je construis cette assez modulaire (modules Foo et Bar sont assez autonomes), et je suis le prototypage d'un système où il y aura des dizaines de ces types, une liste verbeux centralisée des constructeurs de type semble ... mal.

Il est tout à fait possible (probable?) Que mon approche est tout simplement erronée et que c'est nulle part près de la bonne façon de structurer de toute façon la hiérarchie de type - mais comme je ne peux pas avoir un seul type de data quelque part et ajouter un nouveau constructeur pour le type dans chacun de ces modules (?) Je suis perplexe sur la façon d'obtenir un joli « plug-in » se sentir sans avoir à redéfinir les fonctions utilitaires simples à chaque fois. Toutes les suggestions et toutes sortes ont accepté avec joie.

Était-ce utile?

La solution

Vous pouvez avoir des implémentations par défaut des fonctions dans une classe de types, par exemple

class Parameterized a where
    params :: a -> [Param]
    fromParams :: [Param] -> a
    addParam :: a -> Param -> a
    addParam x par = fromParams $ par : params x
    -- ... other functions

instance Parameterized Foo where
    params (Foo pars) = pars
    fromParams = Foo

Cependant, votre conception ne semble suspect.

Autres conseils

Vous pouvez utiliser un newtype pour Foo et Bar, puis dériver la mise en œuvre en fonction de la structure sous-jacente (dans ce cas, les listes) automatiquement -XGenerializedNewtypeDeriving.

S'ils sont vraiment censés être structurellement similaire, mais vous avez codé d'une manière qu'aucun code peut être partagé, alors c'est un motif suspect.

Il est difficile de dire de votre exemple ce serait la bonne façon.

Pourquoi avez-vous besoin à la fois Foo et Bar si elles sont structurellement similaires? Tu ne peux pas aller juste avec Foo?

Si, pour une raison quelconque, je ne vois pas,

vous avez besoin à la fois Foo et Bar vous devrez utiliser une classe de type mais vous pouvez nettoyer le code à l'aide de modèle Haskell.

Quelque chose comme

$(superDuper "Foo")

pourrait générer le code

data Foo = Foo [Param]

instance Parameterization Foo where
    addParam (Foo params) param = Foo (param:params)

superDuper :: String -> Q [Dec]
superDuper n =
  let name = mkName n
      dataD = DataD [] name [] [NormalC name [] ] -- correct constructor here
      instD = InstanceD [] ... -- add code here
  return [dataD, instD]

Ce serait au moins se débarrasser du codage de la plaque de la chaudière.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top