Tracks
/
Roc
Roc
/
Übungen
/
Lens Person
Lens Person

Lens Person

Mittel

Anleitung

Verwende Linsen, um verschachtelte Records zu aktualisieren (spezifisch für Sprachen mit unveränderlichen Daten).

Felder von verschachtelten, unveränderlichen Records zu aktualisieren ist ziemlich lästig. Der Code für solche Fälle ist genauso umständlich wie die Struktur tief ist. Wenn du zum Beispiel eine Person hast, die eine Adresse enthält, die eine Straße hat, die eine Nummer hat, dann musst du zum Aktualisieren der Nummer eine neue Straße mit der neuen Nummer erzeugen, dann eine neue Adresse mit der neuen Straße und schließlich eine neue Person mit der neuen Adresse. Schon verwirrt?

Eine Lösung für dieses Problem ist die Verwendung von Linsen.

Implementiere mit Linsen mehrere Funktionen für den Zugriff auf Records. Die Testsuite erlaubt es dir außerdem, ganz auf Linsen zu verzichten, damit du mit verschiedenen Ansätzen experimentieren kannst.

Linsen werden in Roc nicht oft verwendet, weil die Syntax zum Aktualisieren von Records die meisten Anwendungsfälle schon gut abdeckt. Betrachte zum Beispiel diesen verschachtelten Record:

bob = { hair: { color: Brown, length: Long }, mouth: Smiling, height: 6.0 }

Jetzt schneiden wir Bob die Haare:

new_bob = { ..bob, hair: { ..bob.hair, length: Short } }

Ganz ohne Linsen! Dennoch: Auch wenn Linsen in Roc selten sind, sind sie in anderen Sprachen wie Haskell und Scala weit verbreitet. Vielleicht bist du also neugierig, wie man sie in Roc umsetzen könnte. Außerdem ist es eine gute Möglichkeit, fortgeschrittene Roc-Features wie parametrisierte Typen und Records, die Funktionen enthalten, zu üben. Hier ist eine Möglichkeit, eine Linse in Roc darzustellen:

Lens(whole, part) := {
	get : whole -> part,
	set : whole, part -> whole,
}

Das ist ein parametrisierter Typ: whole und part können beliebige Typen sein. whole ist typischerweise ein Record, während part ein beliebiger Feldtyp sein kann. Das Feld get enthält eine Funktion, die einen bestimmten part des übergebenen whole zurückgibt, während das Feld set eine Funktion enthält, die eine Kopie des übergebenen whole zurückgibt, nur dass ein bestimmter part durch den übergebenen part ersetzt wird.

Hier ist zum Beispiel eine Linse, mit der du die Haare einer Person abrufen oder setzen kannst:

hair_lens = {
	get: |person| person.hair,
	set: |person, hair| { ..person, hair },
}

Mit dieser Linse können wir den Fokus auf Bobs Haare legen:

bob_hair = bob |> hair_lens.get

Und seine Haare können wir so ändern:

new_hair = { ..bob.hair, length: Short }
new_bob = bob |> (hair_lens.set)(new_hair)

Hinweis: Die runden Klammern um hair_lens.set sind erforderlich. Ohne sie würde Roc im Typ von hair_lens nach einer Funktion namens set suchen.

Linsen werden besonders nützlich, wenn du anfängst, sie zu komponieren, denn dann kannst du tief verschachtelte Teile lesen und setzen. Das geht mit einer Funktion wie dieser:

compose : Lens(outer, inner), Lens(inner, part) -> Lens(outer, part)

Sobald diese Funktion implementiert ist, kannst du damit eine Linse für die Haarlänge erstellen:

length_lens = {
	get: |hair| hair.length,
	set: |hair, length| { ..hair, length },
}
hair_length_lens = compose(hair_lens, length_lens)

Jetzt kannst du Bob noch einmal die Haare schneiden:

set_hair_length = hair_length_lens.set
new_bob = bob |> set_hair_length(Short)

Vielleicht möchtest du auch eine Funktion implementieren, mit der du eine beliebige Transformation auf einen verschachtelten Teil anwenden kannst:

over : Lens(whole, part), whole, (part -> part) -> whole

Sobald sie implementiert ist, kannst du over so verwenden:

cut_hair = |current_length| match current_length {
	Long => Short
	Short => Bald
	Bald => Bald
}
new_bob = hair_length_lens |> over(bob, cut_hair)

In dieser Übung prüfen die Tests, ob dein Code verschiedene verschachtelte Teile des folgenden verschachtelten Records ändern kann:

test_person : Person
test_person = {
	name: { forenames: "Jane Joanna", surname: "Doe" },
	birth: {
		born_at: { street: "Longway", house_number: 1024, place: "Springfield", country: "United States" },
		born_on: { year: 1984, month: 4, day: 12 },
	},
	address: { street: "Shortlane", house_number: 2, place: "Fallmeadow", country: "Canada" },
}

Wir empfehlen, die Tests zuerst mit der Syntax zum Aktualisieren von Records von Roc zu bestehen und es dann mit Linsen zu versuchen.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Roc Exercism

Bereit, mit Lens Person zu starten?

Melde dich bei Exercism an, um Roc mit 125 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.