Il y a au moins 4 bibliothèques que je suis conscient de la fourniture de verres.
La notion d'objectif est qu'il offre quelque chose d'isomorphe à
data Lens a b = Lens (a -> b) (b -> a -> a)
fournir deux fonctions: un getter et un setter
get (Lens g _) = g
put (Lens _ s) = s
soumis aux trois lois:
Tout d'abord, que si vous mettez quelque chose, vous pouvez le sortir
get l (put l b a) = b
Deuxième que l'obtention d', puis la configuration ne change pas la réponse
put l (get l a) a = a
Et la troisième, en mettant deux fois, c'est la même chose que de mettre une fois, ou plutôt, que la deuxième de mettre les victoires.
put l b1 (put l b2 a) = put l b1 a
Notez que le type de système n'est pas suffisant pour vérifier ces lois pour vous, alors vous devez vous assurer que vous-même n'importe quelle lentille de mise en œuvre que vous utilisez.
La plupart de ces bibliothèques fournissent également un tas d'extra combinators sur le dessus, et généralement une certaine forme de modèle haskell machines pour générer automatiquement des lentilles pour les champs de simples types d'enregistrement.
Avec cela à l'esprit, nous pouvons nous tourner vers les différentes implémentations:
Implémentations
fclabels
fclabels est peut-être le plus facilement motivé à propos de l'objectif des bibliothèques, parce que son a :-> b
peuvent être traduits directement du type ci-dessus. Il fournit une Catégorie d'instance pour (:->)
ce qui est utile car il permet de composer des lentilles. Il fournit également un lawless Point
type qui généralise la notion d'objectif utilisé ici, et certains de plomberie pour traiter isomorphisms.
Un obstacle à l'adoption d' fclabels
, c'est que le paquet inclut le modèle-haskell de plomberie, de sorte que le paquet n'est pas Haskell 98, et il exige aussi l' (assez controversée) TypeOperators
extension.
données accesseur
données accesseur est un peu plus populaire que fclabels
, en partie parce qu'il est Haskell 98. Cependant, le choix de la représentation interne me fait vomir dans ma bouche un petit peu.
Le type T
il utilise pour représenter une lentille est à l'intérieur défini comme
newtype T r a = Cons { decons :: a -> r -> (a, r) }
Par conséquent, afin d' get
la valeur d'un objectif, vous devez soumettre une valeur non définie pour le 'a' argument! Ce qui me frappe comme un incroyablement laid et ad hoc de mise en œuvre.
Cela dit, Henning a inclus le modèle-haskell plomberie pour automatiquement générer les accesseurs pour vous dans un groupe séparéde données accesseur-modèle " .
Il a l'avantage d'une décemment grand ensemble de paquets qui utilisent déjà, Haskell 98, et fournissant la Category
de l'instance, de sorte que si vous ne payez pas attention à ce que la saucisse est faite, ce package est en fait assez raisonnable de choix.
lentilles
Ensuite, il y a les lentilles de paquet, qui observe que l'objectif est de fournir un état de monade homomorphism entre deux état monades, par definining lentilles directement en tant que tels monade homomorphisms.
Si elle a effectivement pris la peine de fournir un type pour ses lentilles, ils ont un rang-2 type:
newtype Lens s t = Lens (forall a. State t a -> State s a)
En conséquence, je n'aime pas cette approche, car elle inutilement les ricains vous de Haskell 98 (si vous voulez un type de fournir à vos lentilles dans l'abstrait) et vous prive de l' Category
exemple pour les verres, ce qui vous permet de le composer avec d' .
. La mise en œuvre nécessite également multi-paramètre type de classes.
Note, toutes les autres lentilles bibliothèques mentionnés ici de fournir quelques combinator ou peut être utilisé pour fournir ce même état focalisation effet, si rien n'est gagné par l'encodage de vos lentilles directement dans ce mode.
En outre, le côté-conditions énoncées au début n'ont pas vraiment de jolie expression sous cette forme. Comme fclabels' c'est un modèle haskell méthode pour la génération automatique de lentilles pour un type d'enregistrement directement dans le paquet principal.
En raison de l'absence d' Category
de l'instance, le baroque de l'encodage et de l'exigence de modèle-haskell dans le paquet principal, c'est ma préférée au moins de la mise en œuvre.
les données de la lentille
[Edit: Comme de la version 1.8.0, ils ont déménagé de la comonad-transformateurs de paquet de données-lens]
Mon data-lens
paquet fournit des lentilles en termes de Stocker comonad.
newtype Lens a b = Lens (a -> Store b a)
où
data Store b a = Store (b -> a) b
Élargi ceci est équivalent à
newtype Lens a b = Lens (a -> (b, b -> a))
Vous pouvez afficher ce que l'affacturage l'argument commun de le getter et le setter retourner une paire composée de la suite de la récupération de l'élément, et un setter pour mettre une nouvelle valeur. Cette offre le calcul de l'avantage que le "setter" ici peut recycler une partie du travail permet d'obtenir la valeur, ce qui rend plus efficace "modifier", fonctionnement que dans l' fclabels
définition, en particulier lorsque les accesseurs sont enchaînés.
Il ya aussi une belle justification théorique de cette représentation, parce que le sous-ensemble de 'Objectif', des valeurs qui satisfont les 3 lois déclaré au début de cette réponse sont précisément les objectifs pour lesquels le enveloppés fonction est un "comonad coalgebra" pour le magasin comonad. Cela transforme 3 poilu lois pour une lentille l
à 2 joliment pointfree équivalents:
extract . l = id
duplicate . l = fmap l . l
Cette approche a d'abord été constaté et décrit dans Russell O'Connor Functor
est Lens
comme Applicative
est Biplate
: présentation de Multidisque à bain d'huile et a été blogué sur basé sur un preprint par Jeremy Gibbons.
Il comprend également un certain nombre de combinators pour travailler avec des objectifs strictement et un peu de bouillon de lentilles pour des récipients, tels que Data.Map
.
Si les lentilles en data-lens
forme d'un Category
(à la différence de l' lenses
package), sont Haskell 98 (contrairement à fclabels
/lenses
), sont sain d'esprit (contrairement à l'extrémité arrière de l' data-accessor
) et de fournir un peu plus d'une mise en œuvre efficace, data-lens-fd
fournit les fonctionnalités pour travailler avec MonadState pour ceux qui sont prêts à sortir de Haskell 98, et le modèle-haskell machines est maintenant disponible via data-lens-template
.
Mise À Jour 6/28/2012: Autre Objectif De La Mise En Œuvre Des Stratégies
Isomorphisme Lentilles
Il y a deux autres objectifs codages vaut la peine d'examiner. Le premier donne une belle façon théorique, à la vue d'un objectif comme un moyen de briser une structure dans la valeur du champ, et "tout le reste'.
Étant donné un type pour isomorphisms
data Iso a b = Iso { hither :: a -> b, yon :: b -> a }
tels que des membres valides satisfaire hither . yon = id
, et yon . hither = id
On peut représenter un objectif avec:
data Lens a b = forall c. Lens (Iso a (b,c))
Ils sont surtout utiles comme une façon de réfléchir sur le sens de lentilles, et nous pouvons les utiliser comme un outil de raisonnement pour expliquer les autres lentilles.
van Laarhoven Lentilles
Nous pouvons modéliser des objectifs tels qu'ils peuvent être composées avec (.)
et id
, même sans Category
exemple en utilisant
type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a
comme le type de nos objectifs.
Ensuite, la définition d'un objectif est aussi simple que:
_2 f (a,b) = (,) a <$> f b
et vous pouvez vérifier par vous-même que la fonction de la composition de la lentille de la composition.
J'ai récemment écrit sur la façon dont vous pouvez continuer à généraliser van Laarhoven lentilles pour obtenir la lentille familles qui peuvent changer les types de champs, tout en généralisant cette signature
type LensFamily a b c d = forall f. Functor f => (c -> f d) -> a -> f b
Cela a pour conséquence regrettable que la meilleure façon de parler de lentilles est d'utiliser le rang 2 de polymorphisme, mais vous n'avez pas besoin d'utiliser cette signature directement lors de la définition des objectifs.
L' Lens
j'ai défini ci-dessus pour _2
est en fait un LensFamily
.
_2 :: Functor f => (a -> f b) -> (c,a) -> f (c, b)
J'ai écrit une bibliothèque qui comprend des lentilles, des lentilles de familles, et d'autres généralisations y compris les getters, setters, les plis et les traversals. Il est disponible sur le hackage que l' lens
package.
Encore une fois, un grand avantage de cette approche est que les responsables de la bibliothèque peut effectivement créer des lentilles dans ce style dans vos bibliothèques sans encourir aucune lentille de la bibliothèque de dépendance que ce soit, par la simple fourniture de fonctions de type Functor f => (b -> f b) -> a -> f a
, de leurs types " a " et "b". Cela réduit considérablement le coût de l'adoption.
Puisque vous n'avez pas besoin d'utiliser effectivement le package de définir de nouveaux objectifs, il faut beaucoup de pression sur mon préoccupations antérieures sur la tenue à la bibliothèque Haskell 98.