Il existe au moins trois bibliothèques populaires pour accéder et manipuler des champs d'enregistrements. Ceux que je connais sont: un accesseur de données, des fclabels et des lentilles.
Personnellement, j'ai commencé avec data-accessor et je les utilise maintenant. Cependant, récemment, sur haskell-cafe, il y avait une opinion selon laquelle les étiquettes étaient supérieures.
Par conséquent, je suis intéressé par la comparaison de ces trois bibliothèques (et peut-être plus).
lens
package a les fonctionnalités et la documentation les plus riches, donc si vous ne vous souciez pas de sa complexité et de ses dépendances, c'est la voie à suivre.Réponses:
Il y a au moins 4 bibliothèques que je suis conscient de fournir des lentilles.
La notion de lentille est qu'elle fournit quelque chose d'isomorphe à
fournissant deux fonctions: un getter et un setter
soumis à trois lois:
Premièrement, si vous mettez quelque chose, vous pouvez le récupérer
Deuxièmement, obtenir puis régler ne change pas la réponse
Et troisièmement, mettre deux fois équivaut à mettre une fois, ou plutôt, que le deuxième put gagne.
Notez que le système de type n'est pas suffisant pour vérifier ces lois à votre place, vous devez donc vous en assurer vous-même quelle que soit l'implémentation d'objectif que vous utilisez.
Beaucoup de ces bibliothèques fournissent également un tas de combinateurs supplémentaires sur le dessus, et généralement une forme de machinerie de gabarit pour générer automatiquement des lentilles pour les domaines de types d'enregistrement simples.
Dans cet esprit, nous pouvons nous tourner vers les différentes implémentations:
Implémentations
fclabels
fclabels est peut-être la bibliothèque de lentilles la plus facile à raisonner, car elle
a :-> b
peut être directement traduite dans le type ci-dessus. Il fournit une instance de catégorie(:->)
qui est utile car elle vous permet de composer des objectifs. Il fournit également unPoint
type qui généralise la notion de lentille utilisée ici, et un peu de plomberie pour traiter les isomorphismes.Un obstacle à l'adoption de
fclabels
est que le package principal inclut la plomberie template-haskell, donc le package n'est pas Haskell 98, et il nécessite également l'TypeOperators
extension (assez non controversée) .accesseur de données
[Modifier:
data-accessor
n'utilise plus cette représentation, mais est passée à une forme similaire à celle dedata-lens
. Je garde ce commentaire, cependant.]data-accessor est un peu plus populaire que
fclabels
, en partie parce qu'il est Haskell 98. Cependant, son choix de représentation interne me fait un peu vomir dans la bouche.Le type
T
qu'il utilise pour représenter une lentille est défini en interne commePar conséquent, pour déterminer
get
la valeur d'une lentille, vous devez soumettre une valeur indéfinie pour l'argument «a»! Cela me semble être une mise en œuvre incroyablement laide et ad hoc.Cela dit, Henning a inclus la plomberie template-haskell pour générer automatiquement les accesseurs pour vous dans un package séparé ' data-accessor-template '.
Il a l'avantage d'un ensemble décemment grand de packages qui l'utilisent déjà, étant Haskell 98, et fournissant le très important
Category
instance la plus , donc si vous ne faites pas attention à la façon dont la saucisse est fabriquée, ce paquet est en fait un choix assez raisonnable .lentilles
Ensuite, il y a le paquet de lentilles , qui observe qu'une lentille peut fournir un homomorphisme de monade d'état entre deux monades d'état, en définissant les lentilles directement comme de tels homomorphismes de monade.
S'il prenait la peine de fournir un type pour ses objectifs, ils auraient un type de rang 2 comme:
En conséquence, je n'aime plutôt pas cette approche, car elle vous arrache inutilement de Haskell 98 (si vous voulez qu'un type fournisse à vos objectifs dans l'abstrait) et vous prive de l'
Category
instance pour les objectifs, ce qui vous permettrait composez-les avec.
. L'implémentation nécessite également des classes de types à paramètres multiples.Notez que toutes les autres bibliothèques d'objectifs mentionnées ici fournissent un combinateur ou peuvent être utilisées pour fournir le même effet de focalisation d'état, donc rien n'est gagné en encodant votre objectif directement de cette manière.
De plus, les conditions secondaires énoncées au début n'ont pas vraiment une belle expression sous cette forme. Comme pour 'fclabels', cela fournit une méthode template-haskell pour générer automatiquement des objectifs pour un type d'enregistrement directement dans le package principal.
En raison du manque d'
Category
instance, de l'encodage baroque et de l'exigence de template-haskell dans le package principal, c'est ma mise en œuvre la moins préférée.lentille de données
[Edit: À partir de la 1.8.0, ceux-ci sont passés du package comonad-transformers à data-lens]
Mon
data-lens
forfait fournit des lentilles en termes de magasin comonad.où
Développé, cela équivaut à
Vous pouvez voir cela comme la prise en compte de l'argument commun du getter et du setter pour renvoyer une paire constituée du résultat de la récupération de l'élément, et un setter pour remettre une nouvelle valeur. Cela offre l'avantage de calcul que le "setter" ici peut recycler une partie du travail utilisé pour obtenir la valeur, ce qui permet une opération de `` modification '' plus efficace que dans le
fclabels
définition, en particulier lorsque les accesseurs sont enchaînés.Il y a aussi une belle justification théorique pour cette représentation, car le sous-ensemble de valeurs de `` Lens '' qui satisfont les 3 lois énoncées au début de cette réponse sont précisément les lentilles pour lesquelles la fonction enveloppée est une `` comonad coalgebra '' pour le magasin comonad . Cela transforme 3 lois poilues pour une lentille
l
en 2 équivalents bien sans point:Cette approche a été notée et décrite pour la première fois dans Russell O'Connor
Functor
is toLens
asApplicative
is toBiplate
: Introducing Multiplate et a fait l'objet d'un blog sur la base d'une pré-impression de Jeremy Gibbons.Il comprend également un certain nombre de combinateurs pour travailler strictement avec des lentilles et des lentilles de stock pour des conteneurs, tels que
Data.Map
.Donc, les lentilles en
data-lens
forme aCategory
(contrairement à lalenses
package), sont Haskell 98 (contrairement àfclabels
/lenses
), sont saines (contrairement à l'arrière dedata-accessor
) et fournissent une implémentation légèrement plus efficace,data-lens-fd
fournit la fonctionnalité pour travailler avec MonadState pour ceux qui souhaitent sortir de Haskell 98, et la machine template-haskell est maintenant disponible viadata-lens-template
.Mise à jour du 28/06/2012: Autres stratégies de mise en œuvre de l'objectif
Verres d'isomorphisme
Il existe deux autres encodages d'objectifs à considérer. Le premier donne une belle façon théorique de voir une lentille comme un moyen de décomposer une structure dans la valeur du champ, et «tout le reste».
Étant donné un type pour les isomorphismes
de telle sorte que les membres valides satisfassent
hither . yon = id
, etyon . hither = id
On peut représenter une lentille avec:
Celles-ci sont principalement utiles pour réfléchir à la signification des lentilles, et nous pouvons les utiliser comme outil de raisonnement pour expliquer d'autres lentilles.
Verres van Laarhoven
Nous pouvons modéliser des lentilles de telle sorte qu'elles puissent être composées avec
(.)
etid
, même sansCategory
instance, en utilisantcomme le type de nos lentilles.
Ensuite, définir une lentille est aussi simple que:
et vous pouvez valider vous-même que la composition des fonctions est la composition des verres.
J'ai récemment écrit sur la façon dont vous pouvez généraliser davantage les objectifs van Laarhoven pour obtenir des familles de lentilles qui peuvent changer les types de champs, simplement en généralisant cette signature à
Cela a la conséquence malheureuse que la meilleure façon de parler des lentilles est d'utiliser le polymorphisme de rang 2, mais vous n'avez pas besoin d'utiliser cette signature directement lors de la définition des lentilles.
Le que
Lens
j'ai défini ci-dessus pour_2
est en fait unLensFamily
.J'ai écrit une bibliothèque qui comprend des lentilles, des familles de lentilles et d'autres généralisations, notamment des getters, des setters, des plis et des traversées. Il est disponible sur hackage en tant que
lens
package.Encore une fois, un grand avantage de cette approche est que les responsables de la bibliothèque peuvent créer des lentilles dans ce style dans vos bibliothèques sans encourir aucune dépendance de bibliothèque de lentilles, en fournissant simplement des fonctions avec type
Functor f => (b -> f b) -> a -> f a
, pour leurs types particuliers 'a' et 'b'. Cela réduit considérablement le coût de l'adoption.Comme vous n'avez pas besoin d'utiliser le package pour définir de nouvelles lentilles, cela soulage beaucoup de mes préoccupations précédentes concernant la conservation de la bibliothèque Haskell 98.
la source
:->
data-accessor
, puis je l'ai passé à Henning et j'ai arrêté d'y prêter attention. Laa -> r -> (a,r)
représentation me met également mal à l'aise, et mon implémentation d'origine était exactement comme votreLens
type. Heeennnninngg !!