Syntax

Comments

-- a single line comment

{- a multiline comment
   {- can be nested -}
-}

Here's a handy trick that every Elm programmer should know:

{--}
add x y = x + y
--}

Just add or remove the } on the first line and you'll toggle between commented and uncommented!

Literals

-- Boolean
True  : Bool
False : Bool

42    : number  -- Int or Float depending on usage
3.14  : Float

'a'   : Char
"abc" : String

-- multi-line String
"""
This is useful for holding JSON or other
content that has "quotation marks".
"""

Typical manipulation of literals:

True && not (True || False)
(2 + 4) * (4^2 - 9)
"abc" ++ "def"

Lists

Here are four things that are equivalent:

[1..4]
[1,2,3,4]
1 :: [2,3,4]
1 :: 2 :: 3 :: 4 :: []

Conditionals

if powerLevel > 9000 then "OVER 9000" else "meh"

If you need to branch on many different conditions, you just chain this construct together.

if key == 40 then n + 1
else if key == 38 then n - 1
     else n

You can also have conditional behavior based on the structure of algebraic data types and literals

case maybe of
  Just xs -> xs
  Nothing -> []

case xs of
  hd::tl -> Just (hd,tl)
  []     -> Nothing

case n of
  0 -> 1
  1 -> 1
  _ -> fib (n-1) + fib (n-2)

Each pattern is indentation sensitive, meaning that you have to align all of your patterns.

Union Types

type List = Empty | Node Int List

Records

For more explanation of Elm's record system, see this academic paper.

point =                        -- create a record
  { x = 3, y = 4 }

point.x                        -- access field

map .x [point , {x=0 , y=0}]   -- field access function

{ point | x = 6 }              -- update a field

{ point |                      -- update many fields
    x = point.x + 1,
    y = point.y + 1
}

dist {x , y} = sqrt (x^2 + y^2)                       -- pattern matching on fields

type alias Location = { line : Int , column : Int }   -- type aliases for records


Functions

square n = n^2

hypotenuse a b = sqrt (square a + square b)

distance (a , b) (x , y) = hypotenuse (a - x) (b - y)

Anonymous functions:

square = \n -> n^2

squares = List.map (\n -> n^2) [1..100]

Infix Operators

You can create custom infix operators. Precedence goes from 0 to 9, where 9 is the tightest. The default precedence is 9 and the default associativity is left. You can set this yourself, but you cannot override built-in operators.

(?) : Maybe a -> a -> a
(?) maybe default =
  Maybe.withDefault default maybe

infixr 9 ?

Use (<|) and (|>) to reduce parentheses usage. They are aliases for function application.

f <| x = f x
x |> f = f x

dot = scale 2 (move (20 , 20) (filled blue (circle 10)))

dot' = circle 10 |> filled blue |> move (20,20) |> scale 2

note: this is borrowed from F#, which borrowed it from Ocaml, which inspired by Unix pipes

Relatedly, (<<) and (>>) are function composition operators.

The basic arithmetic infix operators all figure out what type they should have automatically.

23 + 19    : number
2.0 + 1    : Float

6 * 7      : number
10 * 4.2   : Float

100 // 2  : Int
1 / 2     : Float

There is a special function for creating tuples:

(,) 1 2              == (1,2)
(,,,) 1 True 'a' []  == (1,True,'a',[])

You can use as many commas as you want.

Let Expressions

Let expressions are for assigning variables, kind of like a var in JavaScript.

let
  x = 3 * 8
  y = 4 ^ 2
in
  x + y

You can define functions and use “destructuring assignment” in "let" expressions too.

let (x , y) = (3 , 4)
    hypotenuse a b = sqrt (a^2 + b^2)
in hypotenuse x y

Let-expressions are indentation sensitive, so each definition must align with the one above it.

Modules

module MyModule exposing (..) 

-- qualified imports
import List                    -- List.map, List.foldl
import List as L               -- L.map, L.foldl

-- open imports
import List exposing (..)               -- map, foldl, concat, ...
import List exposing ( map, foldl )     -- map, foldl

import Maybe exposing ( Maybe )         -- Maybe
import Maybe exposing ( Maybe(..) )     -- Maybe, Just, Nothing
import Maybe exposing ( Maybe(Just) )   -- Maybe, Just

Qualified imports are preferred. Module names must match their file name, so module Parser.Utils needs to be in file Parser/Utils.elm.

Type Annotations

answer : Int
answer = 42

factorial : Int -> Int
factorial n = List.product [1..n]

distance : { x : Float, y : Float } -> Float
distance {x,y} = sqrt (x^2 + y^2)

Type Aliases

type alias Name = String
type alias Age = Int

info : (Name , Age)
info = ("Steve", 28)

type alias Point = { x:Float , y:Float }

origin : Point
origin = { x = 0, y = 0 }