更新Elm中记录内嵌套值的简明方法(0.18)

时间:2017-02-08 16:10:30

标签: record elm

我正在寻找一种简洁的方法来更新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" } }

这似乎有点太多代码只是为了更新嵌套值,你知道是否有更好/更短的方法来实现它?

3 个答案:

答案 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" } }