Core Language

We will cover values, functions, lists, tuples, and records which all correspond pretty closely with structures in JavaScript

Start up elm repl in the terminal:

$> elm-repl  
---- elm repl 0.16.0 -----------------------------------------------------------
 :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
>

> "hello"
"hello" : String

> "hello" ++ " world"
"helloworld" : String

> 2 + 3 * 4
14 : number

> (2 + 3) * 4
20: number

> 9 / 2
4.5 : Float

> 9 // 2
4 : Int

> isNegative n = n < 0
<function> : number -> Bool

> isNegative 4
False : Bool

> isNegative -7
True : Bool

> isNegative (-3 * -4)
False : Bool

>> if True then "hello" else "world"
"hello" : String

> if False then "hello" else "world"
"world" : String

Elm does not have a notion of “truthiness” as in many dynamic languages, where numbers and strings and lists all can be used as boolean values

> over9000 powerLevel = if powerLevel > 9000 then "It's over 9000" else "meh"
<function> : number -> String

> over9000 42
"meh" : String

> over9000 100000
"It's over 9000" : String

Lists can hold many values, and those values must all have the same type

> names = [ "Alice", "Bob", "Chuck" ]
["Alice","Bob","Chuck"] : List String

> List.isEmpty names
False : Bool

> List.length names
3

> List.reverse names
["Chuck","Bob","Alice"]

> numbers = [1,4,3,2]
[1,4,3,2]

> List.sort numbers
[1,2,3,4]

> List.filter (\x -> x % 2 == 0) numbers
[4,2] : List Int
    
> double n = n * 2
<function>

> List.map double numbers
[2,8,6,4]

> List.foldl (\x a -> x + a) 0 numbers
10 : number

In contrast with OOP, Elm does not have a concept of “methods” where your data and logic are tightly coupled. Instead, functions and data exist separately. And to keep things modular, Elm uses modules. So when we say List.isEmpty we are using the isEmpty function from the List module. The List module is full of functions pertaining to lists.

A tuple can hold a fixed number of values, and each value can have any type. A common use is if you need to return multiple values from a function:

> import String
> goodName name = \
|      let y = String.length name
|      in if y  <= 20 then (True, "name accepted!", y) \
|                     else (False, "name was too long", y)

> goodName "Tom"
(True, "name accepted!", 3)

A record is a set of key-value pairs, similar to objects in JavaScript

> point = { x = 3, y = 4 }
{ x = 3, y = 4 }

> point.x
3

> bill = { name = "Gates", age = 57 }
{ age = 57, name = "Gates" }

> bill.name
"Gates"

So we can create records using curly braces and access fields using a dot. Elm also has a version of record access that works like a function. By starting the variable with a dot, you are saying please access the field with the following name, so .name accesses the name field of the record.

> .name bill
"Gates"

> List.map .name [bill,bill,bill]
["Gates","Gates","Gates"]

When it comes to making functions with records, you can do some pattern matching to make things a bit lighter.

> under70 {age} = age < 70
<function> 

> under70 bill
True

> under70 { species = "Triceratops", age = 68000000 }
False

So we can pass any record in as long as it has an age field that holds a number.

It is often useful to update the values in a record.

> { bill | name = "Nye" }
{ age = 57, name = "Nye" }

> { bill | age = 22 }
{ age = 22, name = "Gates" }

It is important to notice that we do not make destructive updates. In other words, when we update some fields of bill we actually create a new record rather than overwriting the existing one. Elm makes this efficient by sharing as much content as possible. If you update one of ten fields, the new record will share all of the nine unchanged values.

Records vs Objects

Records in Elm are similar to objects in JavaScript, but there are some crucial differences: