2017-02-08 25 views
8

Tôi đang tìm một cách súc tích để cập nhật giá trị lồng nhau bên trong bản ghi trong Elm (0,18).Cách ngắn gọn để cập nhật giá trị lồng nhau bên trong bản ghi trong Elm (0,18)

Với ví dụ sau:

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

tôi có thể cập nhật person.name để "Steve" bằng cách sử dụng biểu thức sau đây:

{ person | name = "Steve" } 

Tuy nhiên, tôi đang tìm một cách để cập nhật một lồng nhau giá trị. Ví dụ, tôi muốn cập nhật person.address.city thành "Madrid". Tôi đã thử các cách sau:

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

Trình biên dịch từ chối tất cả các biến thể này. Tùy chọn hợp lệ ngắn nhất tôi thấy là:

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

Điều này có vẻ hơi quá nhiều mã chỉ để cập nhật giá trị lồng nhau, Bạn có biết cách nào tốt hơn/ngắn hơn để đạt được điều đó không?

+0

sử dụng elm-monocle https://github.com/toastal/toast.al-blog/blob/master/posts/code/2017-01-13-playing-with-prisms-for-the- not-so-isomorphic.md – rofrol

Trả lời

8

Ví dụ cuối cùng của bạn với cú pháp let/in ngắn gọn nhất có thể trong Elm 0,18 mà không cần sử dụng các gói bổ sung.

Điều đó đang được nói, trong các ngôn ngữ chức năng, bạn thường sẽ tìm thấy khái niệm về Ống kính hữu ích cho việc cập nhật các bản ghi lồng nhau. Có một gói Elm tại arturopala/elm-monocle cung cấp khả năng xây dựng và thực thi các ống kính để nhận và thiết lập chính xác hơn các giá trị bản ghi lồng nhau.

Sử dụng gói phần mềm đó, bạn có thể xây dựng lên ống kính mà sẽ cho phép bạn làm những điều ngắn gọn như thế này:

personWithUpdatedCity = personCity.set "Madrid" person 

getCityOfPerson = personCity.get person 

Nhược điểm là bạn phải viết tất cả các ống kính đang nối dây chính mình. Trong Haskell, hệ thống dây điện này có thể được thực hiện bởi trình biên dịch. Trong Elm, chúng tôi không có sự sang trọng đó.

Mã Elm cần thiết cho các ống kính trên sẽ là:

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 

Như bạn có thể thấy, nó tẻ nhạt và mã nhiều hơn bạn có thể mong đợi để đặt một giá trị lồng nhau. Do tedium đó, bạn có thể muốn dính vào ví dụ let/in trong thời gian này, trừ khi mã của bạn sử dụng các tập lồng nhau trên toàn bộ địa điểm.

Có một older discussion on the topic làm cho giá trị cài đặt dễ dàng hơn trong Elm ở đây, nhưng nó đã không hoạt động trong một thời gian.

+0

Cảm ơn bạn đã trả lời chi tiết. Tôi hy vọng cho một số cú pháp đường từ Elm cho các loại xây dựng. – jakubr

+2

Hôm nay tôi đã xuất bản một bài đăng về cách tôi tạo đường cú pháp của riêng mình để thực hiện cập nhật bản ghi lồng nhau. https://medium.com/elm-shorts/updating-nested-records-in-elm-15d162e80480?source=collection_home---5------0---------- – wintvelt

+0

Đó là khá trơn, @ wintvelt! Tôi thích khả năng đọc và conciseness của phương pháp lật/đường ống. –

0

Bạn có thể thực hiện một chức năng tiếp nhận một Person (mà là một type alias cho { name: String, address: { city: String, country: String }) và một String và trả về các bản ghi được cập nhật, như thế này:

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

Nếu tôi cần phải làm điều này loại cập nhật một rất nhiều, tôi xây dựng một người trợ giúp

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

và sử dụng nó, ví dụ

updateAddress (\a -> { a | city = Madrid }) model 
Các vấn đề liên quan