如何在 Elm 的模型中添加嵌套元素

How to add a nested element in a model in Elm

是否有执行以下操作的标准方法?

{ model | country =
  { model.country | state =
    { model.country.state | city =
      { model.country.state.city | people =
        model.country.state.city.people ++ [ newPerson ]
      }
    }
  }
}

当然,countrystatecity都是嵌套在model中的记录。我只想在嵌套的 city 记录中添加一个人。

以上实际上不起作用。我在第一次提到 model.country 时收到以下错误:

I am looking for one of the following things:

"'"
"|"
an equals sign '='
more letters in this name
whitespace

我能够让它工作的方法是在每一步简单地调用一个函数:

{ model | country = updateCountry newPerson model.country }

updateCountry person country =
  { country | state = updateState newPerson country.state }

然后 updateStateupdateCity 也一样...

https://github.com/evancz/focus 但请注意小字。您能否使用不同的数据结构来代替,例如

Dict ( String, String, String ) Int

然后

addOne country state city = 
  Dict.update (country, state, city) 
     (\v -> case v of 
               Just v_ -> v_ + 1
               Nothing -> 1)
     database

如果你想做这样的事情,通常最好让记录结构更扁平。
通常:

  • 如果您的操作也是嵌套的,则嵌套记录是可以的:如果您对每个级别都有单独的更新功能。 And/or 如果您只想在自己的级别更新嵌套记录。
  • 如果您需要对某些嵌套记录进行根级访问,那么扁平化会更好。

我倾向于将所有内容存储在根级别,并包含 ID 以供参考。对于您的示例,这看起来像这样:

type alias Model =
  { persons : Dict PersonID Person
  , cities : Dict CityID City
  , states : Dict StateID State
  , countries : Dict CountryID Country
  }

此结构允许您从根级别完全访问所有实体。 所以添加一个人将是:

  { model 
  | persons =
      model.persons
      |> Dict.insert newPersonID newPerson
  }

获取一个国家/地区的所有城市现在需要做更多的工作顺便说一句:

citiesInCountry : Model -> CountryID -> Dict CityID City
citiesInCountry model countryID = 
    let
      statesInCountry =
        model.states
        |> Dict.filter (\id state -> state.countryID == countryID)
    in
      model.cities
      |> Dict.filter (\id city -> Dict.member city.stateID statesInCountry)

所以这取决于您的应用中哪个操作更频繁:

  • 如果你有很多像"get all grandchildren"这样的操作,那么嵌套方法可能会更好
  • 如果你有很多 "update this grandchild",那么扁平的替代方案可能更适合

从今天(0.18.0)开始,记录更新语法中不允许使用任意表达式。

换句话说,您不能在更新期间访问记录的字段:

model = { model.topProperty | childProperty = "Hello" }
          ^^^^^^^^^^^^^^^^^
            Syntax error

它是用于 Elm 编译器的 planned feature,但现在,您应该考虑重构您的模型或使用其中一种冗长的解决方法。

就个人而言,我更喜欢 let..in 表达式,但我从不使用深度高于 3 的记录。

let..in 例子

它看起来超级冗长,但这种方法没有什么不好。当 Elm 编译器将支持更好的语法时,您将能够重构它。

以此为起点,开发一套用于不同级别更新的辅助函数。

let
    -- Deconstruct on every level.
    { model } = init
    { country } = model
    { state } = country
    { city } = state
    { people } = city
in
    { init
        | model =
            { model
                | country =
                    { country
                        | state =
                            { state
                                | city =
                                    { city
                                        | people = "John" :: people
                                    }
                            }
                    }
            }
    }