I need to write a lot of typesafe getters and setters for my model (which is a record). The getters are super-concise and I am happy with that.
getFoo = .foo
This is perfect to use as an inline function without giving it a name:
Maybe.map .foo maybeModel
Only one extra character (.
) over the bare minimum required code (the field name). Cannot get any more boilerplate-free.
But what is the shortest way to write the equivalent setter? Now I do
Maybe.map2 (\x v -> {x | foo = v} ) maybeModel maybeValue
That is now no longer "pointless" and there is a lot of boilerplate around the foo
, most of it hard-to-type special characters.
For clarification, there is a related thread about how to get rid of having to write a separate getter for each field. I'm fine with that, but I just want it to be easier on fingers and eyes.
There is no equivalent short-hand of .foo
syntax and it's something that comes up time and time again.
There are long discussions on the topic going back years on the mailing list if you want to find out more around Evan's reasoning for omitting such a thing (e.g. proposals for making setter syntax like !foo
or other special symbols). Like many of Elm's design decisions, I think the answer boils down to keeping a single way of doing something and keeping it easy for newcomers.
The most concise way of working around the clumsy setter syntax that I've found is to create a single setter for each value of the form:
setFoo : Foo -> Model -> Model
setFoo foo model = { model | foo = foo }
Since the model
value comes last, it makes it easy to compose, either by pipelines:
updateModel : Foo -> Bar -> Model -> Model
updateModel foo bar model =
model
|> setFoo foo
|> setBar bar
Or if you want to be a little more concise, you can use >>
to shorten the above:
updateModel2 : Foo -> Bar -> Model -> Model
updateModel2 foo bar = setFoo foo >> setBar bar
Your Maybe.map2
example could be written like this, where maybeModel
and maybeValue
are swapped from your example:
Maybe.map2 setFoo maybeValue maybeModel
Sure, you have to create a lot of boilerplate setter code, but in my opinion it feels much more natural to work with and looks much better than the setter syntax the language provides.