我正在寻找一种简洁的方法来更新Elm(0.18)中记录内的嵌套值。
给出以下示例:
person = { name = "Steven", address = { country = "Spain", city = "Barcelona" } }
我可以将person.name更新为" Steve"使用以下表达式:
{ person | name = "Steve" }
但是,我正在寻找一种更新嵌套值的方法。例如,我想将person.address.city更新为" Madrid"。我尝试了以下方法:
{ person | address.city = "Madrid" }
{ person | address = { address | city = "Madrid" } }
{ person | address = { person.address | city = "Madrid" } }
编译器拒绝所有这些变化。我看到的最短有效选项是:
let personAddress = person.address in { person | address = { personAddress | city = "Madrid" } }
这似乎有点太多代码只是为了更新嵌套值,你知道是否有更好/更短的方法来实现它?
答案 0 :(得分:15)
使用let
/ in
语法的最后一个示例在Elm 0.18中尽可能简洁,无需借助额外的包。
话虽如此,在函数式语言中,您经常会发现Lenses的概念对于更新嵌套记录很有用。在arturopala/elm-monocle有一个Elm软件包,可以构建和执行镜头,以便更简洁地获取和设置嵌套记录值。
使用该软件包,您可以构建镜头,让您做到这样简洁:
personWithUpdatedCity = personCity.set "Madrid" person
getCityOfPerson = personCity.get person
缺点是您必须自己编写所有镜头接线代码。在Haskell中,这种连线可以由编译器完成。在榆树,我们没有那么奢侈。
上述镜片所需的榆木代码如下:
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
正如您所看到的,它比您设置嵌套值所需的繁琐且更多的代码。由于乏味,您可能暂时坚持使用let
/ in
示例,除非您的代码在整个地方使用嵌套集。
这里有一个older discussion on the topic设置值更容易在Elm,但它已经有一段时间没有活动了。
答案 1 :(得分:9)
如果我需要做很多这样的更新,我会建立一个帮手
updateAddress : ( Address -> Address ) -> Model -> Model
updateAddress fn m =
{m | address = fn m.address }
并使用它,例如
updateAddress (\a -> { a | city = Madrid }) model
答案 2 :(得分:0)
您可以创建一个接收Person
({ name: String, address: { city: String, country: String }
为type alias)和String
并返回更新记录的函数,如下所示:
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" } }