Elm-Lang syntax and basic concepts

In less than 10 mins

Digvijay Upadhyay
5 min readSep 12, 2018

Ellie

If you are new to elm and just want to play around with the code quickly you can go to Ellie it’s just like JSfiddle is for JavaScript.

https://ellie-app.com/new

Comments

-- a single line comment  {- a multiline comment    
{- nestend comment -}
-}

Data Types

-- Boolean 
True, False
-- Int
42
-- Float
3.14
-- Char
'a'
-- String
"abc"
-- multi-line String
"""
This is useful for holding JSON or other content that has "quotation marks".
"""
-- List
[1, 2, 3]
["string1", "string2"]
-- Tuples
-- Tip: try to use tuples for 2 to 3 variables
-- if you need more than 3 variables in a tuple
-- I recommend to use records
(True, 11, "Foo Bar")
(3.14, 9)
-- Record
{ name = "foo bar", age = 28, canVote = True}

Conditional Statements

If..else statements

Please note that elm is not a flexible language, there can not be a if statement without else.

if (condition == True) then
1
else
0

Also the returned value by each branch of if else should be of the same type.

// WRONG: would give you a compile error
if (condition == True) then
"count"
else
0

if..else if..else statement

if (weather == "rainy") then
"Pickup the umbrella."
else if (text == "sunny")
"Pickup the sunglasses."
else
"Do you live on the planet Mercury?"

It goes without saying each branch returns the same value of same data type, and also else is not optional, we are supposed to handle all the possible outcomes.

case statements

I have used case statements more than if else because they are much easier to write and handle the test cases and there’s one more benefit of getting parameters associated to types which we’ll see in Custom Types.

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

_ represents same as else in if..else or the default of the cases.

-- Using case with tuples
-- Following case performs the logical OR operation
case (flag1, flag2) of
(True, True)->
True
(True, False) ->
True
(False, True) ->
True
_ ->
False
-- We could have also written the last condition as
(_, _) ->
False

Records

Records are not exactly but somewhat similar to JavaScript objects with following rules

  • You cannot ask for a field that does not exist.
  • No field will ever be undefined or null.
  • You cannot create recursive records with a this or self keyword
  • (This one is just my recommendation) Try avoiding multilevel records, although you can create it but updating them is a pain.

Defining record

record =
{ name = "Albert Carter", age = 20 }

Updating Record

updatedRecord = { record | age = 21 }

Multilevel Records

You can create multilevel records but I would recommend you not to do it.

multiLevelRecord = 
{
name = "Albert Carter",
age = 20,
address = { line1 = "Nightmare at", street = "Elm Street" }
}

Updating multilevel Records

Updating multilevel record is not straight forward to update and you will not be able to update it directly so we need update it in two step process, first update address and then we need to update the main record

updatedAdd = 
{ multiLevelRecord.address | line1 = "Not a nightmare anymore" }
updatedMultilevelRecord =
{ multiLevelRecord | address = updatedAdd }

Record Type Alias

You can also define type of a record and then re-use it

-- Define the record type
type alias Record = { name: String, age: Int }
-- Create a record
record: Record -- type definition, displays what is the data type
record =
{ name = "Albert Carter", age = 20 }
-- or you can also create it as
record =
Record "Albert Carter" 20

I recommend the first method as it is easier to recognise name-values, in second method sequence is important. I haven’t covered everything with records, just the stuff you need to know. You can read more detailed article here.

Custom Types

Custom types are like enums but more.

type UserStatus = Regular | Visitor

if I use a data type/alias UserStatus it can have only two possible values either Regular or Visitor .

for a record

type alias User =
{ type: UserStatus }
userRecord: User
userRecord =
{ type = Regular }

we can also bind a Type with one or more data variables, let’s say we’d like to know name of the user with each status.

In fact a regular user will have an id, we can also add id to Regular status, we can change the definition to

type UserStatus = 
Regular String Int
| Visitor String
type alias User =
{ type: UserStatus }
userRecord: User
userRecord =
{ type = (Regular "Albert Carter" 20) }

so we know anywhere we get user status as Regular it will have id and name of the user. How can we fetch the user name and id?

userNameAndIdRecord = 
case userRecord.type of
Regular username id ->
{ name = username, id = id }
Visitor username ->
{ name = username, id = -1}

Functions

A typical function in elm is similar to a method or function in any other language, it accepts parameters and provides an output. In elm it’s mandatory to return (or whatever you do is automatically returned) let’s see how

-- a function returning 5
someFunction =
5

In above code, you could also argue that it is a variable. Elm is a functional language, everything works in function.

-- a function returning 5 to the power of "power"
someFunction power =
5^power

In both of the above function automatically returns the output.

Another thing to remember is the function can only have one statement which it automatically returns.

-- this is valid
customOrOperation flag1 flag2 =
case (flag1, flag2) of
(True, True)->
True
(True, False) ->
True
(False, True) ->
True
_ ->
False

since the case above returns only one object/value.

What if I want to write multiple statements in a single function?

When you want to break your method in to multiple statements, you should use Let Expressions like below.

someFunction =
let
twentyFour =
3 * 8
sixteen =
4 ^ 2
in
twentyFour + sixteen

you can write as many as statements inside Let Expressions, you can use if..else or cases or any other functions.

someFunction = 
let
condition =
True
defaultVar =
10
in
if condition then
let
var1 =
2
var2 =
3
in
var1 + var2
else
defaultVar

Type Annotations

One of the most important and useful features of elm language. I recommend to declare type annotations always, it can help you understand a nature of a function.

I have dedicated a whole article to this feature, which you can refer here.

Ports

There will be times when you need to perform some operations in JavaScript space, in such times, you can use ports to send an object out to JavaScript and accept an object from JavaScript.

I would recommend you to learn once you have good understanding of elm’s basic concepts and understand using ports after that.

But if you are in a rush refer this article. I will add an article explaining ports in future.

--

--