2017-09-21 3 views
0

Le modèle de mon application Elm contient des enregistrements imbriqués. Je les configure actuellement avec des fonctions immuables normales.Enregistrements dynamiques dans Elm 0.18

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 } 

Je voudrais généraliser les Setters afin que je puisse les définir dynamiquement. Quelque chose comme ceci:

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 

Quelle est la manière la plus semblable à l'orme de faire ceci?

+2

Actuellement, il n'est pas possible de créer une fonction setter comme vous pouvez le faire pour un getter (par exemple '.myField'). Voir cette discussion: https://github.com/elm-lang/elm-compiler/issues/984 – Sidney

Répondre

2

Comme déjà mentionné, il n'y a pas de syntaxe intégrée pour définir un champ d'un enregistrement.

En raison de cette limitation, je pense que votre code actuel est correct. Mais si certains autres champs apparaissent dans le Settings, il peut être utile d'extraire la logique d'accès à un champ imbriqué dans une fonction distincte:

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 } 

Dans le cas où une imbrication plus profonde apparaît, vous pourriez trouver focus library utile. Here est un exemple de son utilisation.

Dans votre cas actuel, la bibliothèque va modifier votre code à ceci:

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) 

Mais les solutions ne sont pas parfaits, puisque vous devez définir un setter personnalisé pour chaque champ.

+0

Je peux voir pourquoi cette restriction existe; il est beaucoup plus difficile de taper des variables dynamiques comme ça. Haskell impose-t-il la même restriction? –

+1

oui, le morceau de code que vous avez fourni (à propos de l'accès dynamique d'un champ dans un enregistrement) est difficile à vérifier –