Elm 0.18 中的动态记录

Dynamic records in Elm 0.18

我的 Elm 应用程序中的模型有一些嵌套记录。我目前正在使用普通的不可变函数设置它们。

Types.elm

type alias Model =
    { settings : Settings
    ...
    }

type alias Settings = 
    { username : String
    , password : String
    ...
    }

App.elm

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        SetUsername username ->
            ( model |> setUserName username, Cmd.none )

        SetPassword password ->
            ( model |> setPassword password, Cmd.none )

Setters.elm

setUserName : String -> Model -> Model
setUserName username model =
    let
        oldUserSettings =
            model.userSettings

        newUserSettings =
            { oldUserSettings | username = username }
    in
        { model | userSettings = newUserSettings }


setPassword : String -> Model -> Model
setPassword password model =
    let
        oldUserSettings =
            model.userSettings

        newUserSettings =
            { oldUserSettings | password = password }
    in
        { model | userSettings = newUserSettings }

我想概括设置器,以便我可以动态地设置它们。像这样:

setUserSettings : String -> String -> Model
setUserSettings field variable model =
    let
        oldUserSettings =
            model.userSettings

        newUserSettings =
            { oldUserSettings | field = variable }
    in
        { model | userSettings = newUserSettings }

setUserName : String -> Model -> Model
setUserName value model =
  setUserSettings username value

setPassword : String -> Model -> Model
setPassword value model =
  setUserSettings password value

最像榆树的方法是什么?

如前所述,没有用于设置记录字段的内置语法。

由于这个限制,我认为您当前的代码没有问题。但是如果Settings中出现更多的字段,将访问嵌套字段的逻辑提取到一个单独的函数中可能会有用:

setUserName : String -> Model -> Model
setUserName username model =
    setUserSettings model (\r -> { r | username = username })


setPassword : String -> Model -> Model
setPassword password model =
    setUserSettings model (\r -> { r | password = password })


setUserSettings : Model -> (Settings -> Settings) -> Model
setUserSettings model updateSettings =
    let
        oldUserSettings =
            model.userSettings

        newUserSettings =
            updateSettings oldUserSettings
    in
        { model | userSettings = newUserSettings }

如果出现更深的嵌套,您可能会发现 focus library useful. Here 是其用法的示例。

在您当前的情况下,库会将您的代码修改为:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    let
        userSettings =
            create .userSettings (\f r -> { r | userSettings = f r.userSettings })

        username =
            create .username (\f r -> { r | username = f r.username })

        password =
            create .password (\f r -> { r | password = f r.password })

    in
        case msg of
            SetUsername value ->
                ( set (userSettings => username) value model, Cmd.none )

            SetPassword value ->
                ( set (userSettings => password) value model, Cmd.none )

但这两种解决方案都不完美,因为您需要为每个字段定义自定义 setter。