Elm (the language)
Elm (the language)
A purely functional programming language that compiles to JavaScript. Created by Evan Czaplicki around 2012; first major release 0.9 in 2013. Designed for building reliable browser apps without the chaos of typical JS.
Elm is genuinely small and genuinely loved by people who've used it, but adoption stalled around 2018. The language hasn't shipped a major version since 0.19 (2018). The community is still active; new code is rare. Elm-the-language is mostly historical now.
What makes Elm important to remember anyway: its ideas won everywhere else. The Elm Architecture (Model / Update / View) is now the dominant pattern in reactive UI design across React (Redux), Bubbletea (Go), tui-realm (Rust), Compose Multiplatform, .NET MVU. Elm-the-language is niche; TEA is mainstream.
What Elm looks like
A counter app, complete:
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main = Browser.sandbox { init = init, update = update, view = view }
type alias Model = Int
type Msg = Increment | Decrement
init : Model
init = 0
update : Msg -> Model -> Model
update msg model =
case msg of
Increment -> model + 1
Decrement -> model - 1
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
Three functions: init, update, view. A type for the model, a type for messages. That's the entire app.
The runtime calls view on the current model to produce HTML, intercepts events, calls update with the corresponding message, gets a new model, re-renders. Loop forever.
Distinguishing properties
- Purely functional. No mutable state, no side effects in user code. All effects are explicit (commands and subscriptions; see Side Effects via Cmd).
- Strong static types with full inference. No type annotations required for most code; the compiler infers them. Type errors are notoriously friendly compared to other static-typed languages.
- No null, no undefined. Optional values via
Maybe. Errors viaResult. The compiler ensures you handle both branches. - No runtime exceptions. "If it compiles, it runs" — Elm's marketing claim, mostly true. Exceptions in production come from JS interop, not Elm itself.
- Immutable everything. Data structures are persistent (structural sharing).
- Single dialect. No build configuration, no plugin ecosystem to choose. One way to write Elm.
- Compiles to JavaScript for the browser. No server-side Elm.
Why adoption stalled
Several factors, all common in niche languages:
- No backwards compatibility. Elm 0.19 (2018) broke a lot of 0.18 code, with limited migration tooling. Communities don't tolerate this well.
- Slow release cadence. Years between releases. Users with real projects need predictability.
- Single-maintainer bottleneck. Evan Czaplicki resists "drive-by" contributions; PRs sit indefinitely. The community fragmented after 0.19.
- JS interop is restricted. Elm 0.19 removed native modules, which had let users wrap arbitrary JS libraries. The replacement (ports) is principled but more cumbersome.
- No strong commercial backer. Compare to TypeScript (Microsoft), ReScript (Facebook then Bloomberg), or even Reason (Facebook).
The result: a beautiful language with a small, devoted user base that doesn't grow.
Why the ideas won anyway
Three reasons:
- The Elm Architecture is portable. It's a pattern, not a language feature. Anyone can implement it in any language. Redux did. Bubbletea did. tui-realm did. The pattern outgrew the language.
- The friendly-error-messages principle. Elm's compiler errors are now widely cited as the gold standard. Rust, Roc, Gleam, and many others explicitly model their error UX on Elm's.
- The "no runtime exceptions" promise. Functional programming with sound types had been an academic curiosity; Elm proved it could be a daily-use experience for a normal frontend developer. That validation seeded the modern wave (Roc, Gleam, ReScript, F#'s renaissance).
Successors and inheritors
Languages that explicitly cite Elm as inspiration:
- Roc (roc-lang.org) — Richard Feldman (former Elm core), aiming for "Elm with platforms beyond the browser." Pre-1.0.
- Gleam (gleam.run) — type-safe Erlang. Acknowledges Elm-like compilation messages and patterns.
- F#'s Elmish — F# implementation of the Elm Architecture for Fable (F#-to-JS).
- Bucklescript / ReScript — different language ancestry but similar functional-on-JS niche.
Why this note exists
Elm is the right first place to send anyone trying to understand the Model/View/Update pattern. The language is small, the syntax is clear, the semantics are honest. Reading the official Elm guide for an hour is the fastest way to internalise The Elm Architecture — even if you'll never write Elm in production.
After that hour, you'll start seeing TEA-shaped code everywhere: in React/Redux apps, in Bubbletea TUIs, in lazydap's reducer, in Compose Multiplatform apps, in .NET MVU. The pattern is universal; the language gave it the cleanest possible expression.
See also
- The Elm Architecture — the pattern Elm popularised
- Pure Functions and Immutable State — the foundation Elm rests on
- Side Effects via Cmd — how Elm handles I/O
- React's Evolution Toward Elm — the cross-pollination
- MVU in Other Ecosystems — Bubbletea, tui-realm, Compose
- Elm's website: https://elm-lang.org/
- The Elm guide: https://guide.elm-lang.org/
- Evan Czaplicki's "Let's be mainstream!" (2015): https://www.youtube.com/watch?v=oYk8CKH7OhE