4 votes

Comment puis-je représenter des données avec des champs optionnels au niveau du type?

Je travaille sur des données pour le flux de contrôle qui a une valeur (polymorphe, peut être n'importe quoi), et il pourrait également avoir une fonction de validation qui vérifie si la valeur est toujours bonne, et pourrait avoir une fonction qui "rafraîchit la valeur" (retourne de nouvelles données avec une nouvelle valeur).

En Haskell vanille, cela peut ressembler à ceci :

data MyData a = MyData
  {value :: a
  ,validator :: Maybe (a -> Bool)
  ,refresher :: Maybe (MyData a -> MyData a)}

Ce que je veux en réalité, ce sont ces types :

data Refreshable = Refreshable | NotRefreshable
data Validatable = Validatable | NotValidatable
MyData (r :: Refreshable) (v :: Validatable)

Je l'ai fait pour Refreshable mais seulement avec Refreshable. Je veux le faire aussi avec Validatable mais je rencontre un problème avec les constructeurs. Juste pour Refreshable, j'ai besoin d'avoir deux constructeurs, un pour des données rafraîchissables et un autre pour des données non rafraîchissables. Avec la validation, je devrai avoir 4 constructeurs ! (pour les rafraîchissables et validables, pour les non rafraîchissables et validables, pour les validables et non rafraîchissables, et pour les non rafraîchissables et non validables). Et imaginez si j'ai besoin d'un autre champ facultatif plus tard. Encore pire : presque tous les champs sont les mêmes sauf ceux qui changent, il y a donc beaucoup de duplication.


J'ai également essayé de résoudre le problème avec des classes de types / familles de types.
Par exemple, MyData 'Refreshable 'NotValidatable devient simplement Refreshable data => data et je pourrais instancer MyData ou simplement le supprimer pour des données plus spécifiques qui peuvent être une instance.

C'est également problématique, car ce ne sont plus vraiment des champs ; c'est-à-dire que je ne peux pas prendre une donnée sans validateur et la changer en la même donnée avec un validateur (pas au niveau du type).


C'est probablement un problème XY ; je pense qu'une approche plus propre serait de créer des types de données comme Refreshable a et Validatable a et de les composer dans MyData mais je ne sais pas comment faire cela. Je ne peux pas les envelopper car l'ordre changerait tout.

Y a-t-il un moyen propre de faire cela ? ou devrais-je juste me contenter des 4 constructeurs ? ou peut-être que Haskell n'est pas encore prêt pour ce type de choses ? (sans jeu de mots :P).

6voto

chi Points 8104

Est-ce que quelque chose comme cela satisferait vos exigences ?

import Control.Applicative (Const)
import Data.Functor.Identity

data MyData kv kr a = MyData
  {value :: a
  ,validator :: kv (a -> Bool)
  ,refresher :: kr (MyData a -> MyData a)}

-- exemples
type FullData a      = Data Identity   Identity   a
type EmptyData a     = Data (Const ()) (Const ()) a
type ValidableData a = Data Identity   (Const ()) a

Un certain enrobage/déballage est requis (pour Identity).

Il est possible de définir des alias mnémoniques type Present = Identity et type Missing = Const (), avec quelques extensions en plus.


Alternativement,

data MyData (v :: Opt) (r :: Opt) a = MyData
  {value :: a
  ,validator :: Validator v a
  ,refresher :: Refresher r a}

data Opt = Yes | No

type family Validator (o :: Opt) a where
    Validator Yes = (a -> Bool)
    Validator No  = ()
-- etc.

-- exemples
type FullData a      = Data Yes Yes a
type EmptyData a     = Data No  No  a
type ValidableData a = Data Yes No  a

2voto

MasterMastic Points 3868

J'ai fini par obtenir une variation de la réponse de chi :

data Opt = Oui | Non

type family TOpt (o :: Opt) a where
  TOpt Oui a = a
  TOpt Non a = ()

Et ensuite quelque chose comme ceci :

data MyData (v :: Opt) a = MyData
  {val :: a
  ,validator :: TOpt v (MyData 'Oui a -> Bool)
  ...
  }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X