I used to do most of my coding in C++. When I moved to python I found, despite loving the formalism of C++, that yes things did seem to be faster to get going.
But I now strongly believe that “rapid prototyping” is an illusion. You might write the code quickly, but you still spend ages debugging/investigating why it isn’t doing what you expected.
Having embraced Elm I now physically cringe every time I have to write something in Python (or other languages). It really irks me just how many times I have to go through the “modify → run → check → find bug → modify → …” loop.
Elm’s incredible type system really does mean that 90% of code works first time. But it’s not just the type system that achieves this - it’s the elm architecture (TEA). Separation of concerns between view
and update
frees up a huge amount of ‘mental space’ for juggling more important concerns within your program.
When I first started using Elm, I liked the type system but couldn’t really understand that it was that much better than anything else I’d previously done - after all it’s a fairly straightforward and simple concept (even if some types can get quite complex individually). But since then I have found myself writing code that I would never have even attempted before as it would have seemed just too complicated an undertaking. The only way you can really understand the power of TEA and a Elm/Haskell/ML-like type system is to use it for a bit.
One of the dangers of the “rapid prototyping” mindset is this absolute fear of “boilerplate”. But honestly it takes a matter of seconds to write most ‘boilerplate’ in Elm. That’s really not where most of your time is ever going to go in a programming task. We all catch ourselves doing it - “oh this would just be faster if I could just avoid having to write all this first…”, and we nearly always spend more time thinking about it than it would have taken to just type it! It’s truly embarrassing how many times I’ve caught myself doing this!! [I think part of it is that we are (correctly) always looking to eliminate duplicated code, so that we only need to modify things in one place. But boilerplate isn’t really the same as duplicated code, it’s just the formalism of the language we’ve chosen.]
Adding the case statements to an update
function, really honestly isn’t going to slow down your experiments in Elm, but it will get you thinking in the TEA approach and the only way you’ll really start to understand the power of that approach is by using it, so I’d really really advise against this idea of starting one way and then redoing things “the proper way” once they get too big. I don’t think it’s helpful in the long term.
Huge apologies if this seems super-opinionated! I just wanted to share how much doing things ‘the right way’ has actually increased my power as a programmer, without my even realising how it was doing so. There really has been a huge amount of thought by Evan and those who contributed to Elm as to why this methodology should be the way Elm works/is supposed to be used.
Finally I’d just add that if update
functions get too large for your liking it’s really easy to split them up by areas of concern. for example one way you could do it…
type Msg
= GameMessage GameMsg
| SettingsMessage SettingsMsg
| ChatMessage ChatMsg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GameMessage gMsg ->
gameUpdate gMsg model
SettingsMessage sMsg ->
settingsUpdate sMsg model
ChatMessage cMsg ->
chatUpdate cMsg model
gameUpdate : GameMsg -> Model -> ( Model, Cmd Msg )
gameUpdate gMsg model =
...