2017-02-08 2 views
8

Ich bin auf der Suche nach einer kurzen Möglichkeit zum Aktualisieren eines geschachtelten Werts in einem Datensatz in Elm (0,18).Kurze Möglichkeit zum Aktualisieren eines geschachtelten Werts in einem Datensatz in Elm (0.18)

das folgende Beispiel Gegeben:

person = { name = "Steven", address = { country = "Spain", city = "Barcelona" } } 

I person.name aktualisieren kann "Steve" mit dem folgenden Ausdruck:

{ person | name = "Steve" } 

Allerdings bin ich nach einer Möglichkeit, eine verschachtelte zu aktualisieren Wert. Zum Beispiel möchte ich person.address.city auf "Madrid" updaten. Ich habe folgendes versucht:

{ person | address.city = "Madrid" } 
{ person | address = { address | city = "Madrid" } } 
{ person | address = { person.address | city = "Madrid" } } 

Der Compiler weist alle diese Variationen zurück. Die kürzeste gültige Option ich sehe, ist:

let personAddress = person.address in { person | address = { personAddress | city = "Madrid" } } 

Dies scheint nur ein bisschen zu viel Code, um einen verschachtelten Wert zu aktualisieren, Wissen Sie, ob es eine bessere/kürzere Weg, dies zu erreichen?

+0

Verwendung Ulme-Monokel https://github.com/toastal/toast.al-blog/blob/master/posts/code/2017-01-13-playing-with-prisms-for-the- not-so-isomorphic.md – rofrol

Antwort

8

Ihr letztes Beispiel mit der let/in Syntax ist so knapp wie möglich in Elm 0.18, ohne auf zusätzliche Pakete zurückgreifen zu müssen.

Das gesagt, in funktionalen Sprachen finden Sie oft das Konzept der Objektive nützlich für die Aktualisierung verschachtelter Datensätze. Es gibt ein Elm-Paket unter arturopala/elm-monocle, das die Möglichkeit bietet, Linsen zu konstruieren und auszuführen, um verschachtelte Datensatzwerte präziser zu erhalten und zu setzen.

dieses Paket verwenden, können Sie Linsen aufbauen könnten, die Sie tun prägnante Dinge wie diese lassen würde:

personWithUpdatedCity = personCity.set "Madrid" person 

getCityOfPerson = personCity.get person 

Der Nachteil ist, dass Sie sich alle Linsen Verdrahtung Code zu schreiben. In Haskell kann diese Verdrahtung vom Compiler durchgeführt werden. In Elm haben wir diesen Luxus nicht.

Der Elm Code für die oben genannten Linsen benötigt dies wäre:

addressCityLens : Lens Address String 
addressCityLens = 
    Lens .city (\cn a -> { a | city = cn }) 

personAddressLens : Lens Person Address 
personAddressLens = 
    Lens .address (\a p -> { p | address = a }) 

personCity : Lens Person String 
personCity = 
    compose personAddressLens addressCityLens 

Wie Sie sehen können, ist es mühsam und viel mehr Code als Sie einen verschachtelten Wert gesetzt werden erwartet. Aus diesem Grund möchten Sie vielleicht vorerst an das Beispiel let/in denken, es sei denn, Ihr Code verwendet überall verschachtelte Sätze.

In Elm gibt es einen older discussion on the topic, um den Einstellwert zu vereinfachen, aber er war einige Zeit nicht aktiv.

+0

Vielen Dank für die ausführliche Antwort. Ich hoffte auf einen syntaktischen Zucker von Elm für diese Art von Konstrukten. – jakubr

+2

Heute habe ich einen Beitrag veröffentlicht, wie ich meinen eigenen syntaktischen Zucker für verschachtelte Datensatzaktualisierungen erstellen kann. https://medium.com/elm-shorts/updating-nested-records-in-elm-15d162e80480?source=collection_home---5------0---------- – wintvelt

+0

Das ist ziemlich glatt, @wintvelt! Ich mag die Lesbarkeit und Prägnanz der Flip/Piping-Methode. –

0

können Sie eine Funktion machen, die eine Person empfängt (das ist ein type alias für { name: String, address: { city: String, country: String } ist) und ein String und gibt den aktualisierten Datensatz, wie folgt aus:

updateCityAdress : Person -> String -> Person 
updateCityAddress person newCity = 
    { name: person.name, address = { country: person.address.country, city = newCity } 

> updateCityAddress person "Madrid" 
> { name = "Steven", address = { country = "Spain", city = "Madrid" } } 
5

Wenn ich diese Art der Aktualisierung ein tun müssen Lot, ich baue einen Helfer

updateAddress : (Address -> Address) -> Model -> Model 
updateAddress fn m = 
    {m | address = fn m.address } 

und verwenden Sie es, z

updateAddress (\a -> { a | city = Madrid }) model 
Verwandte Themen