Notes on Elm programming language

Sergii Riabokon
4 min readDec 22, 2022

Elm is a functional programming language specifically designed for a frontend. Here are short quick notes about its discovery.

Installation and running

Installing with brew:

brew install elm

Starting new projects is done with elm init

mkdir elm-project
cd elm-project
elm init

Built-in REPL starts by

elm repl

Local server on http://localhost:8000 starts with a command

elm reactor

To compile

elm make src/Main.elm

All of the imported modules in Main.elm are compiled automatically. Produced are html and js files. To produce only the JS file:

elm make src/Main.elm --optimize --output=elm.js

Packages are installed with

elm install elm/http

Core language

Functions are defined without commas in arguments and surrounding parenthesis. They have no return statement.

> isEven n = remainderBy 2 n == 0
> isEven 3
false

Instead of != comparing /= used. Instead of ! operator ‘not’.

Anonymous functions are defined with \ before arguments and -> before a functions code:

> isOdd = \n -> remainderBy 2 n /= 0
<function> : Int -> Bool

> isOdd 2
False : Bool

> isOdd 3
True : Bool

String are concatenated with ++

> "Good " ++ "morning"
"Good morning" : String

Prefix syntax is possible

> (++) "Good " "morning"
"Good morning" : String

All operations like length are contained within String module.

Common data structures are tuples, lists and records (maps).

Lists are defined with square brackets [] and commas

> fruits = ["apple", "banana", "oranges"]
["apple","banana","oranges"] : List String
> List.length fruits
3 : Int

Lists can contain only the same type elements.

Tuples are defined with parenthesis ()

> colors = ("red", "green", "yellow")
("red","green","yellow")
: ( String, String, String )

Tuples are allowed to contain different type elements. Tuples length is limited to three elements.

Records (maps) are defined with curly brackets {}

> user = {name = "John", city = "London", age = 30}
{ age = 30, city = "London", name = "John" }
: { age : number, city : String, name : String }

Central concept of Elm is a type system. Custom types in elm operates like enums with symbols (keywords) that start with capital letters listed with |:

type User = Anonymous | Registered | Admin

Common thing is to set an alias to a type:

type alias Colors = String

Aliases might be attached to records (maps):

type UserType = Anonymous | Registered | Admin

type alias CurrentUser = {userType: UserType, name: String}

johny = {status = Registered, name = "John"}

The above can be rewritten to a type notation:

type UserType = Anonymous String | Registered String | Admin String

johny = Registered "John"

All functions are curried automatically

> multy a b = a * b
<function> : number -> number -> number

The above actually means

<function> : number -> (number -> number)

It is possible to partly call a function

> multy a b = a * b
<function> : number -> number -> number

> multy3 = multy 3
<function> : number -> number

> multy 3 5
15 : number

Partial functions can be combined with << and >>

> multi a b = a * b
<function> : number -> number -> number

> multi2 = multi 2
<function> : number -> number

> subtract a b = a - b
<function> : number -> number -> number

> subtract5 = subtract 5
<function> : number -> number

> multiSubtract = multi2 << subtract5
<function> : number -> number

> multiSubtract 10
-10 : number

Pipes |> and <| can be applied to redirect arguments:

> 5 |> (+) 2
7 : number

> (+) 2 <| 5
7 : number

Modules are starting with exporting global scope name

module Main exposing (..)

or

module Main exposing (main)

to litereally list names to export.

One of the common construct is case associated with concrete type

type Msg = Increment | Decrement

update msg model =
case msg of
Increment ->
model + 1

Decrement ->
model - 1

Function and all the other declaration first states signatures, that implementation:

> divide: Int -> Int -> Float
| divide a b = toFloat a / toFloat b
|
<function> : Int -> Int -> Float
> divide 8 4
2 : Float
>

Analysing samples

Documentation example shows follow:

module Main exposing (..)

import Html exposing (main_)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)

main =
Browser.sandbox { init = 0, update = update, view = view }

type Msg = Increment | Decrement

update msg model =
case msg of
Increment ->
model + 1

Decrement ->
model - 1

view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]

Obvious drawbacks are:

  • HTML model is written in the same file as a programming code;
  • Events dispatcher follows PubSub pattern (update msg model function) instead of Observer pattern.

References

--

--

Sergii Riabokon

Technical blog about programming and related stuff. Mostly contains my personal discoveries and some memorable notes.