J'aurais pu stocker des indices de polygone dans la scène actuelle, un index de point glissé dans Polygon et le remplacer à chaque fois. Mais cette approche n'est pas évolutive - lorsque les niveaux de composition atteignent 5 et plus, le passe-partout devient insupportable.
Vous avez absolument raison, cette approche n'est pas évolutive si vous ne pouvez pas contourner le passe-partout . Plus précisément, le passe-partout pour créer une toute nouvelle scène avec une minuscule sous-partie a changé. Cependant, de nombreux langages fonctionnels fournissent une construction pour gérer ce type de manipulation de structure imbriquée: les lentilles.
Un objectif est essentiellement un getter et un setter pour des données immuables. Un objectif se concentre sur une petite partie d'une plus grande structure. Étant donné un objectif, il y a deux choses que vous pouvez faire avec lui - vous pouvez afficher la petite partie d'une valeur de la structure plus grande, ou vous pouvez définir la petite partie d'une valeur d'une structure plus grande sur une nouvelle valeur. Par exemple, supposons que vous ayez un objectif qui se concentre sur le troisième élément d'une liste:
thirdItemLens :: Lens [a] a
Ce type signifie que la plus grande structure est une liste de choses, et la petite sous-partie est l'une de ces choses. Compte tenu de cet objectif, vous pouvez afficher et définir le troisième élément de la liste:
> view thirdItemLens [1, 2, 3, 4, 5]
3
> set thirdItemLens 100 [1, 2, 3, 4, 5]
[1, 2, 100, 4, 5]
La raison pour laquelle les lentilles sont utiles est qu'elles sont des valeurs représentant des getters et des setters, et que vous pouvez les abstraire de la même manière que vous pouvez utiliser d'autres valeurs. Vous pouvez créer des fonctions qui renvoient des objectifs, par exemple une listItemLens
fonction qui prend un nombre n
et renvoie un objectif affichant le n
e élément dans une liste. De plus, les lentilles peuvent être composées :
> firstLens = listItemLens 0
> thirdLens = listItemLens 2
> firstOfThirdLens = lensCompose firstLens thirdLens
> view firstOfThirdLens [[1, 2], [3, 4], [5, 6], [7, 8]]
5
> set firstOfThirdLens 100 [[1, 2], [3, 4], [5, 6], [7, 8]]
[[1, 2], [3, 4], [100, 6], [7, 8]]
Chaque objectif résume le comportement pour traverser un niveau de la structure de données. En les combinant, vous pouvez éliminer le passe-partout pour traverser plusieurs niveaux de structures complexes. Par exemple, en supposant que vous ayez un scenePolygonLens i
qui affiche le i
e polygone dans une scène et un polygonPointLens n
qui affiche le nth
point dans un polygone, vous pouvez créer un constructeur d'objectif pour se concentrer uniquement sur le point spécifique qui vous intéresse dans une scène entière, comme ceci:
scenePointLens i n = lensCompose (polygonPointLens n) (scenePolygonLens i)
Supposons maintenant qu'un utilisateur clique sur le point 3 du polygone 14 et le déplace de 10 pixels vers la droite. Vous pouvez mettre à jour votre scène comme suit:
lens = scenePointLens 14 3
point = view lens currentScene
newPoint = movePoint 10 0 point
newScene = set lens newPoint currentScene
Celui-ci contient joliment tout le passe-partout pour parcourir et mettre à jour une scène à l'intérieur lens
, tout ce dont vous avez besoin, c'est de savoir ce que vous voulez changer de point. Vous pouvez en outre résumer cela avec une lensTransform
fonction qui accepte un objectif, une cible et une fonction pour mettre à jour la vue de la cible à travers l'objectif:
lensTransform lens transformFunc target =
current = view lens target
new = transformFunc current
set lens new target
Cela prend une fonction et la transforme en "mise à jour" sur une structure de données compliquée, en appliquant la fonction uniquement à la vue et en l'utilisant pour construire une nouvelle vue. Revenons donc au scénario de déplacement du 3e point du 14e polygone vers la droite de 10 pixels, qui peut être exprimé en termes de lensTransform
ceci:
lens = scenePointLens 14 3
moveRightTen point = movePoint 10 0 point
newScene = lensTransform lens moveRightTen currentScene
Et c'est tout ce dont vous avez besoin pour mettre à jour toute la scène. C'est une idée très puissante et fonctionne très bien lorsque vous avez de belles fonctions pour construire des lentilles affichant les éléments de vos données qui vous intéressent.
Cependant, ce sont des trucs assez excentriques actuellement, même dans la communauté de programmation fonctionnelle. Il est difficile de trouver un bon support de bibliothèque pour travailler avec des objectifs, et encore plus difficile d'expliquer comment ils fonctionnent et quels sont les avantages pour vos collègues. Adoptez cette approche avec un grain de sel.