Elm vs. Javascript: Side by Side Code Comparison
Tags: javascript, elm
Published: 2020-05-14 13:28:45

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/evt3071pqswskrv96j9l.png)

I heard several times people feeling uneasy when exposed to the Elm syntax for the first time.

Familiarity plays an important role when looking at a new language and Elm is probably more familiar to Haskell developers than to Javascript developers.

In the tutorial [🍣 Kaiten Sushi 🍣 Approaches to Web Animations](https://dev.to/lucamug/kaiten-sushi-approaches-to-web-animations-306k) I wrote the same animation both in [Elm](https://lucamug.github.io/kaiten-sushi/tutorial-04-vanilla-elm.html) and in [Javascript](https://lucamug.github.io/kaiten-sushi/tutorial-03-vanilla-javascript.html).

Here I will compare the code side by side. I know that is a bit like comparing apples and oranges but, why not?

The code has been adjusted for this comparison so it is not either the best Javascript nor the best Elm.

I also didn’t replicate The Elm Architecture in Javascript because... it was too much.

But enough talking. Let's get to the code







# The **View**

```elm
-- Elm

view model =
    [ img [ id "kaiten", src "svg/background.svg", onClick ClickOnPage ] []
    , div [ id "homeLink" ]
        [ a [ href "https://lucamug.github.io/kaiten-sushi/" ]
            [ img [ src "svg/home.svg" ] [] ]
        ]
    , div [ id "title" ] [ text "04 - VANILLA ELM - CLICK ANYWHERE"]
    , div ([ id "sushi" ] ++ changeStyle model.currentState) [ text "🍣" ]
    ]
```

```html
<!-- HTML -->

<img id="kaiten" src="svg/background.svg" onclick="clickOnPage()">
<div id="homeLink">
    <a href="https://lucamug.github.io/kaiten-sushi/">
        <img src="svg/home.svg">
    </a>
</div>
<div id="title">03 - VANILLA JAVASCRIPT - CLICK ANYWHERE</div>
<div id="sushi">🍣</div>
```

* The Javascript version uses plain HTML. Elm has a `view` function that generates the DOM at runtime through a *Virtual DOM*. It is the analogues of JSX in React but in plain Elm code

* The Elm view needs the text to be the argument of the `text` function. We cannot just put it there similar to HTML or JSX

* In Elm, [for each HTML element there is a correspondent function](https://package.elm-lang.org/packages/elm/html/latest/) that gets two lists as arguments. The first list are the attributes, the second are the children elements

* Because it is just Elm language, we can call functions and use data directly (see the `title` or `changeStyle` for example). Actually in Elm more than `changeStyle` is rather `generateStyle`

* On click Elm sends out the message ClickOnPage while Javascript calls directly the clickOnPage function. Think of messages as kind of events







# The **changeStyle** function

```elm
-- Elm

changeStyle { scale, x } =
    [ style "transform" ("scale(" ++ String.fromFloat scale ++ ")")
    , style "left" (String.fromFloat x ++ "px")
    ]
```

```javascript
// Javascript

function changeStyle(scale, x) {
    sushi.style.transform = "scale(" + scale + ")";
    sushi.style.left = x + "px";
}
```

* `++` vs. `+` to concatenate strings

* In Elm, the view function is called every time the model changes so it is here that we change the style to move the plate of sushi using the Virtual DOM. In Javascript we modify the DOM directly

* In Elm we need to convert types because it is a strictly typed language (String.fromFloat), Javascript does it automatically

* `{ scale, x }` it is a way to deconstruct a record directly. In reality `changeStyle` gets only one argument. Arguments in Elm functions are separated by spaces, not commas





# Elm **Records** vs. Javascript **Objects**

```elm
-- Elm

onTheKaiten =
    { x = 50
    , scale = 1
    }

inTheKitchen =
    { x = 600
    , scale = 0
    }

init =
    { currentState = onTheKaiten
    , animationStart = onTheKaiten
    , target = onTheKaiten
    , animationLength = 0
    , progress = Nothing
    }
```

```javascript
// Javascript

onTheKaiten = {
    x: 50,
    scale: 1
};

inTheKitchen = {
    x: 600,
    scale: 0
};

init = {
    currentState: onTheKaiten,
    animationStart: onTheKaiten,
    target: onTheKaiten,
    animationLength: 0,
    progress: null
}

model = init
```

* In Elm, we use `=` instead of `:`. Also usually commas are at the beginning so that they are aligned vertically and the code seems tidier

* `Model` in Elm contains the entire state of the application. It is a single source of truth enforced by the compiler and is immutable. I use a global model object in Javascript just to make the code look similar, but it carries different meaning. In Javascript it is just a mutable global object





# The **calculateDelta** function

```javascript
// Javascript

previousAnimationFrame = null;

function calculateDelta(timestamp) {
    var delta = null;
    if (model.progress === 0) {
        delta = 1000 / 60;
        previousAnimationFrame = timestamp;
    } else {
        delta = timestamp - previousAnimationFrame;
        previousAnimationFrame = timestamp;
    }
    return delta;
}
```

* This is some boilerplate needed only on the Javascript side because in Elm the delta is coming from the Elm Runtime

* This function determine the amount of time (*delta*) passed between each animation frame






# The **clickOnPage** Function

```elm
-- Elm

clickOnPage model =
    if model.target == onTheKaiten then
        { model
            | target = inTheKitchen
            , animationStart = model.currentState
            , animationLength = 1000
            , progress = Just 0
        }

    else
        { model
            | target = onTheKaiten
            , animationStart = model.currentState
            , animationLength = 1000
            , progress = Just 0
        }
```

```javascript
// Javascript

clickOnPage = function() {
    if (model.target === onTheKaiten) {
        model = {
            ...model,
            target: inTheKitchen,
            animationStart: model.currentState,
            animationLength: 1000,
            progress: 0,
        }
        window.requestAnimationFrame(animationFrame);
    } else {
        model = {
            ...model,
            target: onTheKaiten,
            animationStart: model.currentState,
            animationLength: 1000,
            progress: 0
        }
        window.requestAnimationFrame(animationFrame);
    }
};
```

* In Elm all functions are pure so can only rely on input arguments. This is why we are passing the model. In the Javascript example we made “model” global so we don’t need to pass around

* Also the syntax `{ model | a = b }` is used to copy a record changing only the value of key `a` into `b`. We need to copy records as it is not possible to change them in place. `model.a = b` is not a valid construct. All data is immutable in Elm

* In Elm, requestAnimationFrame is handled in different places. It is activated in subscriptions when `progress` becomes `Just 0`. In Javascript we just call it from here





# The **animationFrame** function

```elm
-- Elm

animationFrame model delta =
    case model.progress of
        Just progress ->
            if progress < model.animationLength then
                let
                    animationRatio =
                        Basics.min 1 (progress / model.animationLength)

                    newX =
                        model.animationStart.x
                            + (model.target.x - model.animationStart.x)
                            * animationRatio

                    newScale =
                        model.animationStart.scale
                            + (model.target.scale - model.animationStart.scale)
                            * animationRatio
                in
                { model
                    | progress = Just <| progress + delta
                    , currentState = { x = newX, scale = newScale }
                }

            else
                { model
                    | progress = Nothing
                    , currentState = model.target
                }

        Nothing ->
            model
```

```javascript
// Javascript

function animationFrame(timestamp) {
    if (model.progress !== null) {
        if (model.progress < model.animationLength) {
            var delta = calculateDelta(timestamp);

            var animationRatio =
                Math.min(1, model.progress / model.animationLength);

            var newX =
                model.animationStart.x +
                (model.target.x - model.animationStart.x) *
                animationRatio;

            var newScale =
                model.animationStart.scale +
                (model.target.scale - model.animationStart.scale) *
                animationRatio;

            model = { ...model,
                progress: model.progress + delta,
                currentState: { x: newX, scale: newScale }
            }

            changeStyle(newScale, newX);
            window.requestAnimationFrame(animationFrame);
        } else {
            model = { ...model,
                progress: null,
                currentState: model.target
            }
        }
    }
}
```

* This is the function that recalculates the new position of the sushi plate. Similar on both sides. The Javascript version needs to change the style calling `changeStyle` while this is handled in the view by Elm

* Also Javascript needs to call `requestAnimationFrame` at the end, so that the animation keeps going

* Javascript is done








# Extra Elm stuff

From there there is the Elm code that wire everything together.








## The **subscriptions**

```elm
-- Elm

subscriptions model =
    case model.progress of
        Just _ ->
            Browser.Events.onAnimationFrameDelta AnimationFrame

        Nothing ->
            Sub.none
```

* Here is where we tell the Elm runtime when or when no to send messages on the animation frame












## The **update** function

```elm
-- Elm

update msg model =
    case msg of
        ClickOnPage ->
            clickOnPage model

        AnimationFrame delta ->
            animationFrame model delta
```

* Here we explain what to do when we receive messages.







## The **Types**

```elm
-- Elm

type Msg
    = AnimationFrame Float
    | ClickOnPage

type alias State =
    { scale : Float, x : Float }

type alias Model =
    { currentState : State
    , target : State
    , animationLength : Float
    , progress : Maybe Float
    , animationStart : State
    }
```
* Type definitions








## The **Elm Runtime** entry point

```elm
-- Elm

main : Program () Model Msg
main =
    sandboxWithTitleAndSubscriptions
        { title = title
        , init = init
        , view = view
        , update = update
        , subscriptions = subscriptions     
```

* Connecting everything to the Elm Runtime using the custom entry point `sandboxWithTitleAndSubscriptions`. Elm provides by default four entry-points ([`sandbox`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox), [`element`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#element), [`document`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#document) and [`application`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#application)) in order of complexity

* What we need for the animation is a combination of those, so I created `sandboxWithTitleAndSubscriptions`. It is similar to `sandbox` but with some extra stuff







# The **sandboxWithTitleAndSubscriptions**

```elm
-- Elm

sandboxWithTitleAndSubscriptions args =
    Browser.document
        { init = \_ -> ( args.init, Cmd.none )
        , view = \model -> { title = args.title, body = args.view model }
        , update = \msg model -> ( args.update msg model, Cmd.none )
        , subscriptions = args.subscriptions
        }
```

* This is the custom defined entry point

# Conclusion

I feel that Elm and Javascript are not that different after all, from a syntax point of view. I hope this post helps to make things less scary.

# The **Code**

* Elm version: [Editable Demo](https://ellie-app.com/8KVh2ZjtFTra1), [Demo](https://lucamug.github.io/kaiten-sushi/tutorial-04-vanilla-elm.html), [Code](https://github.com/lucamug/kaiten-sushi/blob/master/src/Tutorial_04_VanillaElm.elm)

* Javascript version: [Editable Demo](https://codepen.io/lucamug/pen/PoPGpXp), [Demo](https://lucamug.github.io/kaiten-sushi/tutorial-03-vanilla-javascript.html), [Code](https://github.com/lucamug/kaiten-sushi/blob/master/docs/tutorial-03-vanilla-javascript.html)

# Related **Links**

Side-by-side mappings between JavaScript and Elm

* https://elm-lang.org/docs/from-javascript

The Elm Minimal Syntax Reference

* https://elm-lang.org/docs/syntax

A SSCCE (Short, Self Contained, Correct (Compilable), Example) for the entire Elm syntax by [pdamoc](https://github.com/pdamoc)

* https://github.com/pdamoc/elm-syntax-sscce

And its Ellie version

* https://ellie-app.com/8MKgxxwxSW2a1