Blog
Eine (sehr kurze) Einführung in die Optik

Dieser Artikel wurde ursprünglich auf 47deg.com am 01. Oktober 2020 veröffentlicht.
Optiken bieten eine Sprache für den Datenzugriff und die Datenmanipulation. Sie passen sehr gut zum funktionalen Paradigma, da ihr Schwerpunkt auf Kompositivität - Sie bauen komplexere Optiken aus einer kleinen Menge von Bausteinen auf - und Unveränderlichkeit - wann immer Sie eine Operation auf einen Wert anwenden, wird eine Kopie zurückgegeben, im Gegensatz zu veränderbaren Ansätzen. Bibliotheken wie Monocle für Scala, mit einer Portierung nach TypeScript, Arrow Optics für Kotlin, Bow Optics für Swift, Aether für F#, optics und lens für Haskell geben einen Eindruck von der Beliebtheit innerhalb dieser Gemeinschaften.
Als Anfänger in der Optik können Sie jedoch leicht von einem Dutzend Begriffen überfordert werden. Linsen, Prismen, (affine) Traversalen ... sie scheinen alle ähnlich und doch verschieden zu sein. Aber das muss nicht so sein! Hinter diesem Zoo der Optik verbirgt sich eine orthogonale Reihe von Konzepten, die, im richtigen Verhältnis gemischt, die verschiedenen Optiken hervorbringen.
Im Grunde genommen ermöglichen verschiedene Arten von Optiken unterschiedliche Operationen. Jede Operation erfordert immer zumindest die Optik und die Daten, auf die sie angewendet werden soll. Eine der einfachsten ist der Zugriff auf einen Wert innerhalb eines Datensatzes, üblicherweise get oder view genannt. Verschiedene Bibliotheken wählen ihre Syntax je nach dem besten Stil in ihrer jeweiligen Sprache:
data ^. _field -- Haskell + optics
Record.field.get(data) // Kotlin + Arrow Optics
Höhe der Werte
Eine sehr wichtige Idee in diesem Rahmen ist, dass eine Optik im Gegensatz zu den üblichen Getter/Setter-Paaren auf null, eine oder eine unbegrenzte Anzahl von Positionen innerhalb Ihrer Daten abzielen kann.
Wenn wir zum Beispiel eine Änderung mit einer Optik vornehmen, die auf ein Element eines Arrays abzielt (unbeschränkte Menge), wird diese Operation auf jedes Element in der Masse angewendet. Wir haben auch Optiken, die auf optionale Werte abzielen (denken Sie an einen Schlüssel in einem JSON-Dokument, der fehlen könnte). In jedem der drei Fälle kann die Änderung auf zwei Arten durchgeführt werden:
setnimmt einen einzelnen Wert und ersetzt jede Position, auf die die Daten zeigen, durch diesen Wert. Wenn Sie alsosetzusammen mit einer Optik verwenden, die auf alle Elemente in einem Baum abzielt, behält die neue Kopie dieselbe Struktur, aber alle Knoten enthalten jetzt denselben einzelnen Wert.over/modifynimmt eine Funktion auf, die an jeder von der Optik anvisierten Position angewendet wird.
Während Sie Änderungen unabhängig von der Anzahl der Ziele vornehmen, müssen sich die Zugriffsoperationen dieser Tatsache bewusst sein. Aus diesem Grund bieten Optik-Frameworks normalerweise drei Ebenen von "Gettern":
view/getzielt auf genau einen Wert ab, wie eine Eigenschaft in einem Objekt, die wir garantiert haben.preview/getOptionalzielt auf Null- oder Eins-Werte ab, was im Wesentlichen auf einen optionalen Wert hinausläuft, wie ein Index in einem Array, der außerhalb der Grenzen liegen kann.reduce/traverseundtoList/toArrayzielen auf eine unbeschränkte Menge, wie das oben erwähnte Array oder die Werte innerhalb eines Objekts.
Es ist immer sicher, eine Optik auf eine weniger eingeschränkte Weise zu behandeln. Wenn Ihre Optik zum Beispiel genau einen Wert anvisiert, können Sie auch preview oder toList darüber verwenden. Wie wir gleich sehen werden, ist dies für die Zusammensetzung von Optiken wichtig.
Da wir drei "Ebenen der Beträge" und zwei Möglichkeiten für die Einstellung haben (wir können oder nicht), erhalten wir sechs verschiedene Arten von Optiken, plus eine zusätzliche für die Einstellung ohne Zugang. Hier kommt der Zoo der Namen ins Spiel: Fast jedes Feld erhält einen anderen Namen - in einigen Fällen erhält ein und dasselbe Feld je nach Bibliothek unterschiedliche Namen.
set? | Genau 1 | 0 oder 1 | Uneingeschränkt | Kein Zugang |
|---|---|---|---|---|
| Ja | Lens | Optional/AffineTraversal | Traversal | Setter |
| Nein | Getter | PartialGetter/AffineFold | Fold | existiert nicht |
Wie bereits erwähnt, ist einer der Vorteile von Optiken ihre Kompositionalität. Sie können zwei beliebige Optiken komponieren , vorausgesetzt, sie haben eine gemeinsame Operation, und das Ergebnis ist die stärkste Optik, die dieser Operation entspricht. Lassen Sie mich dies anhand eines Beispiels erläutern: Nehmen wir an, Sie möchten eine Optional/AffineTraversal (null oder ein Wert, sowohl get als auch set) mit einer Getter (genau ein Wert, nur get) zusammensetzen. Das Ergebnis muss die Optik sein, die auf null oder einen Wert abzielt (da das Abzielen auf einen Wert zu diesem Fall herabgestuft werden kann) und nur das Holen erlaubt (da Optional/AffineTraversal und Getter eine PartialGetter/AffineFold ist.
Bauherren
Die vorherigen sechs Arten von Optiken können nur auf Werte zugreifen oder diese verändern. Es gibt eine zusätzliche Fähigkeit, die eine Optik haben kann: die Fähigkeit, Werte zu erstellen. Nehmen wir zum Beispiel einen Result erstellen. Das bedeutet, dass wir eine _Error Optik - in diesem Fall ein Prisma - bauen können, die sowohl holen als auch erstellen kann.
_Error # "network failed" -- Haskell + optics
Result.Error.reverseGet("network failed") // Kotlin + Arrow Optics
Dies fügt unserer vorherigen Tabelle noch eine weitere Achse hinzu, je nachdem, ob Sie beim Zugriff garantiert einen Wert erhalten oder nicht. In Anlehnung an unser Beispiel von Result enthält _Error einen optionalen Wert, denn ein Result kann auch _Success sein, und in diesem Fall ist kein Fehlerwert zu erhalten.
| Genau 1 | 0 oder 1 | Uneingeschränkt | Kein Zugang |
|---|---|---|---|
Iso | Prism | existiert nicht | Review
/
ReverseGet |
Interessant ist, dass Sie, wenn Sie eine Möglichkeit zum Abrufen und Erstellen anbieten können, auch eine Möglichkeit zum Einstellen oder Ändern anbieten. Aus diesem Grund befinden sich sowohl Iso als auch Prism auch im set/modify Teil der Hierarchie.
Die gesamte Hierarchie
Nach all diesen Diskussionen haben wir zehn interessante Kombinationen von Operationen gefunden, die jeweils einen anderen Namen tragen. Das folgende Diagramm beschreibt ihre Beziehungen, wobei ein Pfeil bedeutet, dass eine bestimmte Optik mehr Funktionen bietet als die übergeordnete, oder umgekehrt, dass sie in diese gegossen werden kann.

Zusammenfassung
Die Optik wird in Kreisen der funktionalen Programmierung immer beliebter, da sie sehr übersichtlich ist und gut mit unveränderlichen Daten funktioniert.
Wenn Sie mehr erfahren möchten, schauen Sie sich den Kurs Optik an, der in der Xebia Functional Academy verfügbar ist.
Verfasst von
Alejandro Serrano Mena
Unsere Ideen
Weitere Blogs
Contact



