Getting started with Elm – functional programming on the frontend

Hello folks, how are you doing? In this post, we’re going to talk about Elm. It will be a great introduction to those who are getting started with Elm.

Versão em português deste post / portuguese version of this post

So, what is Elm? Elm (http://elm-lang.org/) is a functional programming language that compiles to Javascript, made for the development of web app interfaces. In this sense, it can be considered as an alternative to React, but with some particular diferences.

Since it compiles to Javascript, it’s easy to adopt it slowly on your project. You can convert one part of it to Elm and just use the Javascript generated by it.

One of its greatest advantages is that it’s a statically typed language. This means that the compiler checks your code and guarantees it will work, without the possibility of errors or exceptions caused by type inconsistency or undefined variables.

In some tests, Elm was also faster than React.

If that’s enough to call your attention, let’s start using Elm.

Installation

To install it, there are official installers for Windows and Mac on Elm’s website. Another option is to install it through npm. Just click here and follow the instructions, it should be easy.

Elm’s basics

After installing, you can access Elm’s console entering elm-repl in your terminal. This will open the console, where you can run all the commands for the language.

Strings

First, strings. Strings are defined by double quotes. You can’t use single quotes for strings in Elm. If you try it, you will get a helpful error message, saying that you need to change single quotes for double quotes for strings.

> "hi"
"hi" : String
> 'hi'
-- SYNTAX PROBLEM -------------------------------------------- repl-temp-000.elm

Elm uses double quotes for strings. Switch the ' to " on both ends of the string
and you should be all set!

3|   'hi'
        ^
Maybe <http://elm-lang.org/docs/syntax> can help you figure it out.

To concatenate, we use ++:

> "hi" ++ ", " ++ "how are you?"
"hi, how are you?" : String

Numbers and math

Math operations are what you expect… sum, subtraction, multiplication, division and exponentiation all uses common signs already used in other languages:

> 3 + 6
9 : number
> 7 - 4
3 : number
> 8 * 5
40 : number
> 10 / 2
5 : Float
> 5^3
125 : number

Note that Elm’s division is by default a float division. As with Python 3, for integer division, you can use //. For the remainder of a division, we use rem, passing the two numbers to be divided:

> 7 // 2
3 : Int
> rem 8 3
2 : Int

Functions

To write a function, we declare its name and the parameter to be passed, and then, after the = sign, we write the operation to be done. For example, a function isNegative, that returns if a number is negative, would be written like that:

> isNegative n = n < 0
<function> : number -> Bool
> isNegative 99
False : Bool
> isNegative -345
True : Bool
> isNegative (8 - 9)
True : Bool

Note that we call the function passing the parameter right after it, using only spaces. If the function asked for another parameter, we would just hit another space and pass it along, like isNegative 3 4. It’s a nice and clean syntax.

Conditional behavior – If

If you want to work with conditions, you can use an if block.

> if True then "this value is true" else "this value is false"
"this value is true" : String
> if False then "this value is true" else "this value is false"
"this value is false" : String

Unlike some other languages, Elm does not have the concept of truthiness for values that are not a boolean. So, you can’t use, for example, an empty list in a comparison, you must always use a boolean. You’ll get an error message if you try use anything other than a boolean:

> if "" then "this value is true" else "this value is false"
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

This condition does not evaluate to a boolean value, True or False.

3|   if "" then "this value is true" else "this value is false"
        ^^
You have given me a condition with this type:

    String

But I need it to be:

    Bool

Hint: Elm does not have "truthiness" such that ints and strings and lists are
automatically converted to booleans. Do that conversion explicitly.

The error message is well detailed and helpful, and should be able to help you solve the problem.

You can use if blocks together with functions. Check this example:

> over9000 powerLever = \
|   if powerLever > 9000 then "it's over 9000" else "you weakling"
<function> : number -> String
> over9000 50
"you weakling" : String
> over9000 9001
"it's over 9000" : String
> over9000 150000
"it's over 9000" : String

When we use the backslash, elm-repl, understands that you want to continue your code on the next line. For the code to work, you have to put some spaces on the following line, like I added 2 on the above example. In Elm, indentation is part of the syntax and your code.

Lists

Lists are like Javascript arrays, or Python, guess what, lists. They are data structures that hold multiple values / data. In Elm, all of a list values must be of the same type. To define a list, we include its items inside square brackets, separated by commas:

> ["Felipe", "Gustavo", "Gabriel"]
["Felipe","Gustavo","Gabriel"] : List String
> [1, 2, 4, 9]
[1,2,4,9] : List number
> [1, "2", 3]
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

The 1st and 2nd entries in this list are different types of values.

3|   [1, "2", 3]
         ^^^
The 1st entry has this type:

    number

But the 2nd is:

    String

Hint: Every entry in a list needs to be the same type of value. This way you
never run into unexpected values partway through. To mix different types in a
single list, create a "union type" as described in:
<http://guide.elm-lang.org/types/union_types.html>

You can also apply some methods in lists. Let’s check some of them:

> names = ["Felipe", "Gustavo", "Gabriel"]
["Felipe","Gustavo","Gabriel"] : List String
> empty_list = []
[] : List a
> List.isEmpty names
False : Bool
> List.isEmpty empty_list
True : Bool
> List.length names
3 : Int
> List.reverse names
["Gabriel","Gustavo","Felipe"] : List String
> numbers = [1, 30, 10, 9, 29]
[1,30,10,9,29] : List number
> List.sort numbers
[1,9,10,29,30] : List number
> List.member "Felipe" names
True : Bool
> List.member "Jose" names
False : Bool

And you can also use map, to apply a pre-defined function in each item of a list:

> double n = n * 2
<function> : number -> number
> List.map double numbers
[2,60,20,18,58] : List number

For a complete reference of List methods, check this link on Elm’s official website.

Tuples

Tuples are a data structure used to hold a fixed number of values. In a tuple, each element can be of a different type. A possible good use case for a tuple is when you want a function to return more than one value. For example:

> validPassword password = \
|   if String.length password <= 6 then \
|     (False, "Password must have more than 6 characters") \
|   else \
|     (True, "Password is valid")
<function> : String -> ( Bool, String )
> validPassword "abc"
(False,"Password must have more than 6 characters") : ( Bool, String )
> validPassword "abcd1234"
(True,"Password is valid") : ( Bool, String )

Tuples, however, are kinda limited. For more complex use cases, you should probably use a Record, a data structure similar to Javascript or Python objects, for example.

Record

A record is a set of key-value pairs. To create one, you should define, inside of curly brackets {}, names for the keys and the respective values, after the equal sign and with the pairs separated by commas:

> felipe = { lastName = "Galvao", age = 31, role = "developer" }
{ lastName = "Galvao", age = 31, role = "developer" }
    : { age : number, role : String, lastName : String }
> felipe.lastName
"Galvao" : String
> felipe.age
31 : number

As you can see, a way to access a value in a record is throught the syntax record.key. There is one other way, similar to a function, where the syntax is .key record.

> .role felipe
"developer" : String
> .weight felipe
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

The argument to this function is causing a mismatch.

3|   .weight felipe
           ^^^^^^
This function is expecting the argument to be:

    { b | weight : ... }

But it is:

    { age : ..., role : ..., lastName : ... }

Hint: The record fields do not match up. One has weight. The other has age,
role, and lastName.

As you can see, asking for a key that does not exist in a record raises an error message.

You can also create functions that uses a value on a record. You define the function normally, with the key to be used as a parameter inside curly brackets.

> youngerThan40 { age } = age < 40
<function> : { a | age : number } -> Bool
> youngerThan40 felipe
True : Bool
> abimael = { lastName = "Souza", age = 67 }
{ lastName = "Souza", age = 67 } : { age : number, lastName : String }
> youngerThan40 abimael
False : Bool

To update a value in a record, we use the vertical bar |. Updating a record in Elm is non-destructive, which means that it return a new record with the updated values, leaving all the other key-values unchanged. To update more than one value, just add it after a comma.

> { felipe | lastName = "Skywalker" }
{ lastName = "Skywalker", age = 31, role = "developer" }
    : { age : number, role : String, lastName : String }
> { felipe | lastName = "Skywalker", role = "Jedi" }
{ lastName = "Skywalker", age = 31, role = "Jedi" }
    : { age : number, role : String, lastName : String }

If you want to keep your updates, you must save the new record in a variable. The original record will be kept unchanged

> luke = { felipe | lastName = "Skywalker", role = "Jedi" }
{ lastName = "Skywalker", age = 31, role = "Jedi" }
    : { age : number, role : String, lastName : String }
> luke
{ lastName = "Skywalker", age = 31, role = "Jedi" }
    : { age : number, role : String, lastName : String }
> felipe
{ lastName = "Galvao", age = 31, role = "developer" }
    : { age : number, role : String, lastName : String }

Conclusion

I believe that, with this, we cover most of the basic parts of Elm. In the next post, we will talk about how to use Elm in the real world, building interfaces for a web app.

One comment on “Getting started with Elm – functional programming on the frontend

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *