Elm is pure functional programming language

While preparing for my presentation on ‘Introduction to Elm,’ I tried to prove Elm is a truly functional program by showing this below.

add: Int –> Int –> Int

add x y =

x + y

In the above example, the add function is pure; add function always creates the same output for a given input. So calling Add 2 3 will always return 5. It does not have anything of global scope or side effects. So if I were to introduce a global variable, Elm would go through a compiler error. I was expecting the following code to produce a compiler error.

num: Int

num = 10

add: Int –> Int –> Int

add x y =

num + x + y

Elm did not complain, and it worked. So if I were to pass, add 2 3,  and the result will be15. So in my mind, because the function depends on the outside variable num and someone can change num from outside, the add function is not pure, and thus Elm is not a functional programming language.

So I posted the question in Elm Slack Channel, and @mfeineis answered, “Elm is pure, and num is immutable,” then it hit me. I assumed few things as a normal imperative programmer would. The num is a variable and can be changed easily, but num is not a variable in functional programming, and there is no variable in Elm. So you can’t change the value of num. With that knowledge, if you were to look at the function, the function name is incorrect, it should reflect the function’s proper intention and be ‘addnumwithxandy.’

@jessta reply summed up the answer “ `num` and `10` are interchangeable because Elm is ‘referentially transparent.’ There’s no difference between a value and a variable containing that value.”

For the people, I owe an explanation. Hopefully, this blog helps, and for those helped me on the slack channel, big thanks.

Working with backend services in Elm

Before we go past building trivial solutions, we need to understand one more concept in a trivial example scenario. How do we go about making backend calls? More preciously, how to connect and work with API calls.

We will start out by looking at the final result, and then we will go about building them. We are building a straightforward application that will show the current market value of bitcoin. When you start the application, you will see the following screen.

image

The screen shows the initial data of 0. On pressing the ‘Get Data’ button, the application will make an API call to “https://api.cryptonator.com/api/ticker/ltc-usd”  URL, and display the result as it is on the screen.

image

It does not show the value; instead, it just displays the whole JSON payload as it is. We do not need to worry about parse JSON and other things for our API call discussion, which we will discuss later.

(It will be interesting to come back after a couple of years and recheck the value of bitcoin 🙂 )

Let’s start building the application without an API call first to get the basic screen shown.

First Model:

type alias Model =
{ rate : String
}

initData : Model
initData =
{ rate = “0”
}

We have just one field in the model record, which we kept is as String for the sake of simplicity.

To get started, we create the update section with

type Msg
= GetData

update : Msg -> Model -> Model
update msg model =
case msg of
GetData ->
dummyData

As you can expect, when a user clicks ‘GetData,’ the GetData message is fired by HTML. That is the only event we needed for the application right now. The action event on GetData does nothing but return dummy data. It is not making an API call yet.

The view is simple and straight forward. One label to show the rates and another button that initiates the GetData message.

view : Model -> Html Msg
view model =
div []
[ h3 [] [ text (“Current rate = ” ++ model.rate) ]
, button [ onClick GetData ] [ text “Get Data” ]
]

main =
Html.beginnerProgram { model = initData, view = view, update = update }

When you run the code first time, it will show the init data with 0.

image

On clicking GetData, it shows the dummy data as expected.

image

You can find the whole base code here in the Gist.

Time to add API call. There is one concept here that will look odd, but it is easy to understand. Before we go to the API call, I want to go one more time how HTML and our code interact.

view: Model –> Html Msg

update: Msg –> Model –> Model

In the above code snippet, the view takes the model and creates Html, capable of creating Msgs (like onClick or onInput). What goes into creating the message is hidden inside the Html module, and we do need to worry about; all we need to know when some user action happens, Msg will be triggered, and the ‘update’ function will be called immediately to act on it.

Now the same way, let’s think about API call; when we make an API call, we need to wait for the call to complete, and upon completion, we need to modify the model, and change in the model will update the view. Very simple. One difference between the button click event and the API event is that when API is called, there is a possibility the call might fail. So the result could come back as either successful (Ok resultValue) and the value or comeback with Error (Err _) with an error message. So we need to handle both the scenarios in the code on completion of the API call.

Let’s recap what we discussed so far

  • We need to treat API call similar to Html event calls
  • We need a mechanism to fire a message on completion of an API call
  • On completion, we need to test for the successful execution of API and error scenario

It seems there is a lot but not really. Let’s take baby steps and grasp the concept. To make API calls, we need to install the Http module. Once installed, let’s write the code to make the API call. To make an Http Get, we can use

Http.getString uri

this to make a call and get a string response from the URI. Simple. We could create a function that does it. But we have a problem, this returned string and we need to wait for it completely. We need to modify this somehow so that, on completion, it generates a message which we can act on when we get it. How do we go about doing it?

It is solved by Http.send function. Http.send function will return a message on completion of an HTTP request. So we can modify the previous statement to something like the following

Http.send HttpDataComplete (Http.getString url)

Http.Send is a command, which will initiate the Http.getString, and on completion, it will trigger HttpDataComplete event/msg. So if we were to write a function to do this task, it would be something like this

httpGetData: Cmd Msg

httpGetData, which does not get any input data, will generate a command capable of generating messages. Now let’s look at the implementation

httpGetData =

Http.send HttpDataComplete (Http.getString “https://api.cryptonator.com/api/ticker/ltc-usd”)

We have a function to get data. Now we need to call this function when Get Data button pressed.

Our current update function signature is

update: Msg –> Model –> Model

This reads, update function takes Msg and the current state of the model and generates a new model. But the changes we made when someone presses GetData button to generate the model and create a command that could create msg (like out view definition view : Model –> Html Msg). So our update definition now changes to

update: Msg –> Model –> (Model, Cmd Msg)

With that definition, when a user clicks on ‘GetData,’ we need to call the httpGetData function. We will do it something like the following

case msg of

GetData –>

(model, httpGetData)

if you look at the return state here, following the signature (Model, Cmd Msg).  Now that we made the call, how will we handle the response from getting a data call?

When you make an API call, you can get either a success or failure. On success, we change the rate value with result value. On failure, we need to reset it to initial data. In Elm, we have something Result which helps with this situation. Whenever we perform an operation that could result in success or failure, we need to use Result. With that, when Http make HttpDataComplete message, it will come back with results. So we need to modify the signature of our message to represent this scenario.

HttpDataComplete (Result Http.Error string)

Now in our update function, when we get HttpDataComplete, we need to check for success and failure.

case msg of

HttpDataComplete (Ok data) –>

HttpDataComplete (Err _) –>

The second (Err _) parameter means when you get an error with any value, perform that function.

Now we know we need to handle both the scenario, what should be done when API calls come back successful? All we need to do is modify the rate value in the model with incoming data. Now, if you remember, our update function expects the second parameter to a command generating function. After updating the model, we do not need to initiate any commands, so all we need to pass is Cmd.none. On error, reset the data model to initial data and the success scenario; the pass is no commands. So here is the completed update function.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GetData ->
( model, httpGetData )

HttpDataComplete (Ok val) ->
( { model | rate = val }, Cmd.none )

HttpDataComplete (Err _) ->
( initData, Cmd.none )

We are not done yet, two more things we need to do. When we move to this program model, you might want to load data from back-end API on page load, which means we need to call that function without any program intervention. To accomplish that, we need to move to a different helper function called ‘program’ in Html.

Html.program takes a different set of parameters.

  • The first parameter is the initial data. It includes the initial model and any function that needs to be executed to load data from the back end. In this example, we are not expecting to load any data from API on load. So the values (model, Cmd.none)
  • Second is the view, and there are no changes to it.
  • The third parameter is the update function. No change there.
  • The final parameter is a subscription. For Http example, there is no subscription, so we will create a dummy subscription to satisfy the signature in a minute.

subscription : Model -> Sub Msg
subscription model =
Sub.none

You can find the final source code for this here in this Gist.

Unit testing in Elm

Elm shines in help build applications that do not crash in production.  With static typing, functional nature, and compile-time verification, the odds of breaking Elm application is low. That is said, it does not give you pass not to write tests. I talked in length over time about TDD and why TDD is the best way to build applications. Here are two important things you need to know about TDD

  1. Help build highly maintainable code because the tests guarantee you will not break any other application part.
  2. When done right, TDD will allow you to write very little code to meet the expectations of the requirements. Less code you write, fewer bugs you introduce.

Since everything in Elm is a function, we can test everything.

To setup, we need to install the elm-test package by running

elm package install elm-community/elm-test

This will install all the packages required for building unit tests. Once installed, let’s initialize the project structure for creating unit tests by running the following in the application root directory

elm-test init

This will create a test folder and create a simple test with a ‘todo’ and install associated project dependencies. So before you do anything, run

elm-test

in the root directory, and it should run the default tests and come back with the following message

elm-test 0.18.8
—————

Running 1 test. To reproduce these results, run: elm-test –fuzz 100 –seed 5741
56140

TEST RUN INCOMPLETE because there is 1 TODO remaining

If you get the above message then all set up and ready to start to write testing. If you have a problem setting up the test environment, send me a message.

I love to do my tests using the BDD model, and fortunately, there is a package for that which makes it very readable. When ‘elm-test init’ ran, it created a subdirectory called test to write all your tests. Switch to the test directory and install the BDD package

elm package install rogeriochaves/elm-test-bdd-style

Let’s get our feet wet with nothing fancy but just do normal assertions to see how they all connected. Create a file in the test subdirectory of the project root directory. Create an elm file, I called it ‘SayMyNameTester.elm.”

Let’s start importing the required packages for testing.

import Test exposing (..)
import Expect exposing (..)
import ElmTestBDDStyle exposing (..)

basictest : Test
basictest =
describe “Tests Basic Assertions”
[ test “1. can test equal traditionally”
(\() -> Expect.equal “test” “test”)
, test “2. can test equal traditionally with infix” <|
\() -> Expect.equal “test” “test”
, it “3. can test equal with bdd with infix” <|
expect “test” to equal “test”
, it “4. can test equal with bdd more readable”
(expect “test” to equal “test”)
]

The above test is nothing but doing a simple assertion and “test” equal to “test,” which results in true. But it was written in four different ways to show different ways of writing it and pick the one you are comfortable with and go with it.

When you run Elm-Test at the project root directory, it will execute all the test directory files with Test/Suite function definitions. In our code, we have just one function definition with a Test signature, which is basictest.  So running the Elm-Test should result in something like the following.

image

Let’s walk through each of the tests to understand what is the different

Test 1:

test “1. can test equal traditionally.”
(\() -> Expect.equal “test” “test”)

For starters, the test function takes a string and an Assertions and return a Test. The first parameter is “1. can test equal traditionally” a string. The second parameter is the Assertions “(\() -> Expect.equal “test” “test”)” which returns a Bool. For the people who are coming from the traditional programming language,

\() –>

it represents an anonymous function. Also, the second parameter is enclosed in () to get executed to return the Test result.

Test 2:

This is exactly the same as Test 1, except we replaced the () with an infix notation backward pipe operator <| Backward pipe and forward pipe operator (|>) or interesting operators of the functional programming, which got its roots from the Unix world. You can read more about it here.  For me, this gives more readability than using (). When you compose many operations, when using () might make it a bit hard to read. So I prefer to use the pipe operator

Test 3 & 4:

Test 3 & 4 are similar variations of Test 1 & 2 except, 3 & 4 is written in BDD style. Compare to 1 & 2, 3 & 4 are readable.

My recommendation to use the BDD style option 3 for more readability. When you build a large codebase, help the people who are coming after to do their job easier by writing readable tests. They will thank you for doing it.

I would write a unit test for our project, but I found a great video of Andy Balaam, who walks through the whole unit testing. It is worth checking out.

Reference:

https://medium.com/@_rchaves_/testing-in-elm-93ad05ee1832

https://www.linkedin.com/pulse/single-page-web-apps-elm-part-three-testing-structure-kevin-greene

Setting up Elm development environment

Now that we know some of the core fundamentals and things that make up Elm, let’s start learning by coding. For that, we need to set up the development environment first. It is simple and easy.

I would write step-by-step instructions on how to set up Elm for Atom editor but found this great Git repo. This has all the information needed to setup. Few of things you need to know as you go through them

  • Where ever, it says ‘which’ replace with ‘where’ for Windows
  • If you are using Atom if for some reason something not working, restart Atom
  • linter-elm-make required elm-make.exe. If you are in Windows, make sure when you paste the location of the elm-make, put the whole path and the file name in double quotes like the following

“C:\Program Files (x86)\Elm Platform\0.18\bin\elm-make.exe”

  • When you start a new Elm file, the Elm language may not work, making sure you install elm-package. Atom will complain about missing the JSON file. Execute the following in the command prompt

elm-package install

For those interested in building the apps in Visual Code. Please follow this link https://marketplace.visualstudio.com/items?itemName=Elmtooling.elm-ls-vscode

If you face any problem following the git repo, let me know. I will try to help

Anatomy of Elm

When I started learning Angular, once I understood the angular life cycle, learning Angular was easy. So I would like to do the same here for Elm. Elm’s anatomy is simple. In my previous blog, I mentioned what are all the components constitute elm application.

Elm application has three components, model, view, and update along with the main entry point. Now let’s look at how the whole thing works.

All elm application starts with the entry point main. Elm comes with great bootstrap to get started called beginnerProgram, which hides all the wiring to connect all the parts. So to get started, our main entry point will be something like the following.

main =

beginnerProgram (model = data, view = view, update = messageHandlers}

Here we call the main entry point with the three components, model, view, and update.

The flow of control

clip_image001

  1. On program entry, Elm reads the model data to build the view

2. View generates HTML to render with the data from the model

3. User interacts with view, on each user interaction, the message gets generated.

4. Messages passed to update function. Update look at the incoming messages and create a new instance of the model. (It is a functional program, the model is immutable)

5. The new model will trigger the creation of a new view and the loop repeats.

That is it.

Elm – Basic Architecture

Before you go headfirst into a building, it is good to understand the basic architecture of Elm. Most of the application framework follows one of the MV* pattern. Elm also follows the same design in a different way. Elm has three distinct parts in the application.

Model: As the name suggests, the state of the application. Your data model. The model defines and holds the current state of the application.

View: This holds the visual representation of the application, HTML, as an HTML template.

Update: These events trigger a change in model state, which triggers a change in view.

When you look at Elm application, you potentially will have a view, model, and updates all in a single Elm file. During deployment, Elm will compile this file and spit out HTML and Javascript.

With the above architecture, one would always need to have an entry point to the application, represented by ‘main.’ We will see more details on how they all fit together tomorrow.

Elm – Gradual Learning

I found this video of Evan Czaplicki’s. For anyone interested in learning Elm, this is a good one to listen to. It is very entertaining and educational at the same time. There is a lot of stuff in it, but the one thing that caught my eye was ‘Gradual Learning.’ Learning to do work in a functional language is very difficult for regular programmers. Functional programming is considered a theory, and it has a lot of constraints. The main one that sticks out quickly is immutability. For people coming from C# or Java, it is a no go. It is one of the main reasons there is not much adoption of functional programming. There are pockets of companies and people building the enterprise-grade application in functional programming. Still, the mainstream always found it challenging to adopt.

Coming back to the point which caught my attention, ‘Gradual Learning.’ If you can get developers going quicker and then build on it over time instead of two months, dedicated training will be a big win. For me, Elm provides that, and I have experienced it. It is straightforward to get started without knowing in and out of FP. You can build simple applications in a clean Elm way in five to ten minutes.

Elm provides many boilerplates code for you to get started; over the period, you can begin to peel the layers and learn under the covers. This provides the satisfaction of progression and learning. You will see that first hand as you go through the tutorials.