Good friend of mine said I should learn Elm. — “You’ll get much from it doesn’t matter if you’ll be using it or not.” And that was exactly that kind of friend who no matter what he says always sounds convincing. Probably because of his white beard and blue eyes. Or may be because he is actually almost always right. Elm is 4 year-old grown-up which transpiles into javascript and doesn’t have js fatigue of choosing the ‘right’ framework because actually it is a framework. It is completely functional and is a father of redux: So I sacrificed several promising-to-be-good weekends to get a portion of insights and now happy to share them with you: 1. No runtime errors I will start straight with the hardcore concepts. You can forget about `NaN`, ‘Uncaught TypeError’ or ‘foo is undefined’ annoying error messages in Elm, because it supports which means that at compilation time it checks all of your code to make sure that all of the variables and inputs and outputs from functions match up. This means that if your function accepts only String there is no way you can pass anything else. static type checking And although it is recommended to declare the types like: reps : String -> Int (reps is a function name, String is input type and Int is output type), it is not required, because Elm can automagically figure out all the types itself(which is called and show you appropriate error or success messages after compilation is finished. type inference) 2. Union types One good trick commonly used in Elm — is Union type which allows you to define possible values for the weird or complex type. Say we have an Exercise type which can be one of the following strings: or Pull-up Push-up type Exercise = Pull-up | Push-up and want to make a function which returns number of reps based on the exercise: reps reps : Exercise -> Intreps exercise = case exercise of Pull-up -> 10 Push-up -> 15 What’s important here is that whenever we pass a Union variable as a parameter(which is in this example) we are forced to use expression. And what’s more important is that all the branches of that expression are checked by compiler. Which means that: exercise case case if you forget to handle a case(say with 8 reps), you get an error at compile time. Muscle-up if you mistype any case (say ) by accident, you get an error at compile time. Nice, huh? Psuh-up 3. Edge cases And we are not finished with types yet. Even for me who came to developer world after years of writing requirements specifications and who understand the importance of edge cases, sometimes difficult not to forget them in code. What if value is or json format is incorrect or http request is timed out? undefined Elm takes care of this by introducing Maybe, Result and Task types which are special cases of already familiar Union types. If you have optional field you’ll probably use Maybe: type alias Sportsman = { name : String , age : Maybe Int } which means here that field can present one of 2 values: or —first is empty and second is non-empty value. And because it is Union type, any time you pass this var as a parameter you’re forced to use expression and handle all the variations so you cannot avoid handling null/undefined case: age Nothing Just Int case getReps : Sportsman -> Maybe IntgetReps sportsman = case sportsman.age of Nothing -> Nothing Just age -> Just 12 Same approach is used for and types but instead of optional fields is used for sync operations like parsing json, and is used for async operations like html requests. Both of them can end up in one of the following values: for successful cases and for error cases and same as with we’re always forced to handle both cases. Result Task Task Result Success Failure Maybe 4. Package distribution Another consequence of static typing is automatically enforced versioning. This means that if we create a library and want to share it with others we don’t have to think about its version numbers every time we update our package. Elm will do it for us. Starting from initial version 1.0.0 it will compare code changes for each of the consequent releases of our package and create new version number which does most accurately reflect the changes we made. 🌟 5. Redux Redux is a well known pattern in Javascript nowadays originated in Elm. You can find it in and and I even recently used it in a jQuery project without any modern MV* frameworks. React Angular The idea is you have one single point of trust which is called (Elm) or State(React) or Store(Angular) and you have an interface to update it. In Elm we call this interface (which is Reducer in Javascript world). Model Update So we call function each time we want to change the . Update Model Update function gets current and (Action) as parameters and returns the new . Model Msg Model Lately we use to update our view which is simply if we are talking about website, and we do it by calling the self-titled function — . Model Html View That’s how it looks: Elm architecture And why is it cool? 6. QA and support First of all QA becomes much-much easier. We already know Elm is a functional language which means it provides us 2 functional guarantees: all functions always return the same result given the same argument values all functions don’t create side effects, i.e. don’t change anything outside their own scope And basically this means that having a list of function calls we always can rerun them and will always get the same as a result of rerun. Why? Because no one can change our from outside(remember? no side effects) and because our function always returns the same result if called with the same parameters. Update Model Model Update And this means that any time your QA engineer finds a bug she sends you the log of function calls(which is silently recorded) without manually documenting all the steps. And you instead of manually reproducing all the steps just import it in your app and immediately see the bug on the screen with possibility to move back and forth in the history of actions to see which chain of user actions led to the error. 🌟🌟 Update 6. Lazy loading Second fruit of using redux is lazy loading. What I like about modern javascript frameworks comparing to jQuery is they are declarative. You don’t say in your program how to manipulate the DOM — append, remove, hide, etc. You just describe initial and final states of html — how it should look in different situations, and framework takes care(normally with Virtual DOM) of everything required to update the page so it looks like you want it to. Same for Elm. You simply define the function describing how html shall be rendered based on the current . And then when is changed your function updates the DOM accordingly comparing current and previous states and making corresponding DOM manipulations. View Model Model View Now interesting thing: what if only small part of the was updated and rest of it stayed unchanged? Does it make sense to recalculate and compare the whole DOM? Surely not. Model If we break our view function into pieces so that one function renders header, another renders footer and etc then because of the first function guarantee(always return the same result given the same argument values), we can skip the functions whose input wasn’t changed. We just put keyword in front our function: lazy viewHeader: String -> Html Msg viewHeader name = ...some code view : Model -> Html Msgview model = lazy viewHeader model.loggedUser.name ...other code Here we have a header which shows logged in user name. Once something is changed in the model(user scrolls down and new content is loaded) we don’t want to recalculate header part of the DOM because it is basically same as before. But we want to update content part, because user scrolled it. That’s what do for us — Elm will check that viewHeader input wasn’t changed and skip this part completely but will render the rest. This saves some CPU resources for us and makes rendering faster. lazy 7. Immutability And this leads to another important concept of Elm which is . In previous example we had as an input for our Header function but what if we had something more complex — ? immutability String Object What if we want to compare 2 input objects(new and old)? Then we have to go deep into their fields hierarchy and compare fields one by one. Which is time and resource consuming. On the other side with which requires to create new object each time we change something in it, comparison is as simple as comparing two references which is blazingly fast. Because if references point to the same object then it is the same object and if not then these are two different objects. Cool. Now lazy loading makes even more sense. immutability 8. Even faster But we can do even faster. We all know that typical browser performs repaint 60 times per second as this is how our eyes perceive visual information. There is no need to do repaint more often, because we won’t improve any website look and feel but will be wasting device resources. At the same time we want other operations apart from repaint to work in normal mode either it is an http request or some complex calculations in the background. So how we can achieve this in Elm? Well, we actually don’t have to do anything. It is built-in. Elm uses under the hood to sync view calculations with browser repaints. This means that view function which renders html will not be called more often than 60 times per second and it saves time, CPU resources and mobile device battery. requestAnimationFrame The main reason we can do this in Elm is second function guarantee applied to the function — it doesn’t have any side effects so the only thing it alters — is html. Which means it cannot update the . And while we make function calls to happen only 60 times per second, our still gets updated in real-time without any delays. That’s the purity of pure functions. View Model View Model 9. Fuzz testing As we already can see Elm spends a lot of efforts to prevent errors in our applications so the testing framework used in it is quite elaborate too. One feature I’m excited about and which is coming soon in is . Mocha fuzz testing Idea behind it is running the test case many or several times with randomly generated input. Cool part is all edge cases like zero, empty or negative values are guaranteed to be checked at least once and you can also set some ranges along with their probabilities to be sure that the most important cases are tested more often than others. Fuzz.frequency[ ( 1, Fuzz.intRange -100 -1 ), ( 3, Fuzz.intRange 1 100 )] This can save a lot of manual test writing time but has it’s caveat that tests execution time can be noticeably increased. Conclusion So once we know all these cool things, how can we use them in our normal Javascript life? First, use static typing— Typescript or Flow could be great solutions for that. Typescript allows you to mix it with javascript in the same code base which means you can gradually move your project into static typed without breaking legacy code. It is also provides great IntelliSense support in combination with VSCode. Second — use functional approach where appropriate(state management, rendering, etc). Redux library is a great example. Third, use immutability with help of ready to use libraries like or to advance change detection and improve application performance. Mori immutable.js