Hello Everyone,
I’ve recently finished to set up my company’s new website in Elm (http://hswsnc.com - please keep in mind I’m a developer, not a graphic designer). This was my first real project in Elm beside some hello worlds and examples, and even if it was fairly simple I could already appreciate the improvements over traditional html-javascript programming.
One thing however captured my attention while developing it. I used the elm-bootstrap package for the page’s top navbar. The library requires you to add a field of type Navbar.State to the model (or to otherwise keep track of it) that is used to draw the actual navbar.
Now, adding a field to the Model is obviously not an issue - the navbar has a state, and I would need to keep track of it somehow. What bothers me is having to add an extra message to manage navbar changes in my update function.
update msg model =
case msg of
NavMsg state ->
( { model | navState = state }
, Cmd.none )
Navbar.State is an opaque record, so I can’t do anything with it (as it should be). Nevertheless, these 4 lines of added code are boilerplate, are they not? Ideally, as a user of the library I should have no business in knowing when or how the navbar is updated. Albeit small, it contrasts with the idea of elegance I had of Elm.
This is an example for what I believe is a bigger problem. If an Elm package needs to manage state the programmer must explicitly set up messages like that.
If this was a less functional framework there would be an hidden entity managing the navbar state, maybe an object or a separate thread initiated after a navbarInit() invocation. Of course those are all reasons to move away from those and embrace Elm instead, so it’s not what I am wishing for.
I wanted to hear some opinions about this. Having one user-controlled thread and a single source of truth make these few lines of boilerplate pretty much inevitable, right?
Do you think a solution should be pursued, or is it too small of a problem to bother? Provided you agree with me in the first place in calling it an issue…
I have had the same thoughts, but working with a large app I really have come to appreciate the explicitness of handling the state for these kinds of components. Nothing happens by magic and tracing bugs by looking at messages and state is a breeze.
So I don’t see it as a problem. Also it lets you handle multiple instances without the component author having to explicitly have support for it. Case in point: my drag and drop package.
I take “boilerplate” to mean “you have to type a lot of simple things to get simple things set up”. So I’m always a little lost when people complain about boilerplate, because typing isn’t anyones real problem. Character count is not making or breaking anyone’s projects.
If boilerplate was our biggest problem, that would mean several tremendously bigger problems are solved. It sounds like we would be living some kind of dream.
I guess its all about trade offs, so maybe it doesnt matter how relatively small the benefit of fewer characters is: if it came at no trade-off it would be worthwhile. In your example, you have those 5 lines that tie the NavMsg state to that part of the Model, and you want Elm to assume that relationship exists so you dont have to specify it manually.
How could that assumption be made? Seems impossible to me. There are a million different ways different Msg, and state could be related. None of the millions of different ways are particularly obvious, much less “assume-able”. You would need a human brain that knew how these things were supposed to be related in the end product, in order to know how these Msg and state are related.
I belive what you are describing is called an effect-manager in elm…
With an effect-manager, you can have modules that keep their own internal state. Like the websocket-package in elm 0.18.
But I do not belive effect-managers can be published to package repo. And it might even be removed in elm 0.19 or only available to core and elm-exploration repos.
To me, boilerplate is code that conveys no substantial meaning from the programmer, something that is automatic and does not require any thought process. Those 5 lines are to be set every time I want to use the library and hold no meaning to me; the moment I decide to write import Bootstrap exposing(Navbar) for all intents and purposes they could be implicitly added to my update function.
I strongly disagree with that. A lot of Elm’s beauty is in its conciseness, and conciseness is a major factor in writing good code.
Here, I agree. Not sure exactly how to do it since hiding mechanisms and assuming ties is the exact opposite of Elm’s philosophy, and for good reasons.
You are probably right. In a way the Navbar’s state is like a file descriptor: externally the developer does not know anything about it but the fact that it must be used to reference an element managed by the library. The fact that file descriptors are “indexes” for an internal state while here the programmer keeps track of the state itself matters little.
I don’t think this is correct. If you compare the Elm implementation of Real World to other implementations, the Elm version comes out quite a bit longer than most (sorry haven’t go the stats to hand, but if someone can find them and link?..). Elm’s beauty is perhaps in its explicitness rather than its conciseness.
Sometimes functional programming can be very concise, due operations like map and fold and |> and so on. Some programmers even avoid over using those as they can be too concise; an explicit recursive function can in some cases be easier to read.
I also find Elm to be very concise in my head rather than in terms of the code itself. Once you find the patterns that work best for you, there are less choices to be made. Going from thinking about some program to writing it starts to feel almost mechanical - which is where the feeling that you are writing a lot of boiler-plate comes in. I think it frees you up to focus your efforts on the more interesting and complex parts of the program.
I think worse than the boiler-plate involved in hooking up nested updates (see helper pages suggested by @Namek) , is the boiler-plate involved in writing encoders and decoders. Those really can be long and tedious especially where the mapping to JSON is completely straightforward.
“The fewer lines of code you have, then the probability of finding an error is smaller. You also have a smaller code base to maintain.”
I used to think this was true, but Elm has changed my mind on this. Strong typing and the aim of “no runtime errors” are big factors. Also as I argued above, longer programs can somtimes be more explicit and easier to read and maintain.
Another thought for @Maldus512, since I see you work for a company that makes hardware and software.
Have you ever tried HDL languages like Verilog or VHDL? In those languages when you use a child module you have to ‘wire it into’ the parent module, literally by describing how its input and output wires are to be connected up. I now see this as being very much like how a child module is wired into an Elm program.
I even stopped calling my main modules in Elm Main and now usually call them Top, after the HDL convention of call the top-level module top. Top is typically just wiring some stuff together into a program.
I think comparing lines of code might not be entirely accurate, especially when using elm-format. Maybe non-whitespace characters would be a better metric
Also, Richard Feldman wrote it as an educational example, with lots of comments. The React app on the other hand doesn’t have any comments at all. So you should really filter out the comments as well.
This is still true. Elm is a special case because of “body on the next line” formatting recommendation which adds a lot of lines of code. Also, elm-spa-example is not an example of how you would implement that app if you wanted to win the least lines of code prize. That is an example of how to implement very defensively a web app with a lot of code that is there only for maintainability or protection.
To the extent that it has a detrimental effect, the problem with “boilerplate” code is that it obscures/drowns our the “interesting” code. I would say this issue is real but I don’t know that it is significant.
One of the other dangers with boilerplate is that since it is code you should be able to write without thinking, you get errors due to lack of thinking. Strong typing and completeness checks on case expressions will more or less force one to do all of the right work: add the field to the model and the compiler will more or less force you to write all of the rest provided the embedded model follows the standard API patterns. Or it will right up until we deal with subscriptions. You have to remember to add a call to the subscriptions function if any for the embedded model. I haven’t found a way to get the compiler to enforce that.
That all said, if you are interested in the ways other systems deal with these questions, I recommend spending some time looking at React Hooks as a contrasting still heavily functional approach. It makes it easy to add an embedded model (exposed as a custom hook) but it comes at the price of not having explicit routing for messages. I am not going to argue that Elm or React Hooks is better. Just examples of different answers driven in part by different values around explicitness v reducing boiling plate. Tastes will vary.
You can see the required code at the example repo, which conveniently also uses bootstrap.
You could definitely argue that there is still some boilerplate, but it is all code that is relevant to your app, as opposed to generic code that you don’t care about.