Integrating Elm with Rails

Why Elm? Switching to functional programming languages makes it a better environment for multi-threaded applications. For example, immutability is a powerful functional concept that JavaScript lacks. But in Elm, once created value cannot be changed, thus making a thread-safe environment. Each thread need not worry about other threads when they act on data since these data are represented by immutable objects in Elm. While in other languages it’s hard to catch production errors, Elm offers ‘no run time exceptions’ guarantee.

Friendly error messages

Elm has a reputation for great error messages, being statically typed. Consider the following Elm code
import Html exposing(..)
view orders =
  div [] (List.mapp viewOrder orders)
viewOrder order =
  span [] (text order.name)
Here’s an example for an error message after compiling the Elm code: This is what errors look like in the command line
--NAMING ERROR-----------------------------------------------------list-mapp.elm
Cannot find variable 'List.mapp' .
6| div [] (List.mapp viewOrder orders)
List does not expose 'mapp' . May be you want one of the following?
List.map
List.any
List.map2
List.map3
  • shows the line number in the code which caused the error
  • shows the actual line of code
  • lists the maximum possibilities for correct code
If you have a nice editor with Elm language integration, these error messages can be seen as a pop-up when hovering over the line of code which causes the error. This line of code can be identified easily as it will be red underlined. To start with Elm, we don’t have to worry about the huge libraries as in case of other frameworks. Just install Elm CLI and start coding. In a JavaScript application, we are probably putting together some collection of tools as below.
  • React
  • Redux
  • npm/yarn
  • Webpack
  • Immutable.JS
But Elm addresses these issue by providing all of them built in a single installer. JavaScript languages and browser engines are subjected to constant changes always. It’s not feasible to refactor the application accordingly. Building our application in Elm enables to get more efficient JavaScript code when newer versions of the language are released with compiler updates.

Start with Elm

Elm is a delightful language meant for developer happiness. When you write an Elm app, you write a number of modules in separate files and feed those files to Elm compiler. It gets compiled to JavaScript that’s ready to run the program.

Architecture

Whenever we write an Elm app this architecture will be following. All Elm apps in practice are structured this way. So, we start by writing an init function, whose job is to generate the initial model for our app. This is the complete model for the current state of the front end as it’s running in the browser. The second function is the view that takes the model and generates the Html interface for your web app. In React like frameworks, it lets you generate a virtual DOM. So every time front end is rendered it will calculate the entire user interface and the framework takes responsibility for efficiently updating the changes in the actual browser. Elm also has its own virtual DOM implementation that is really faster compared to React, Angular etc. As part of our interface, we will declare the events we are interested in. If the user does something like click a button, Elm will send the program a message. So we need to write a third function to handle those messages and that’s the update function. This function will take that message and the program’s existing model and put them together in order to generate the new updated model for the program. Then it feeds to the view function to generate the updated interface. This is how Elm looks like.

Install on Rails

Elm is officially supported in Rails 5.1 via the webpacker gem. Be sure to install yarn and Node.js first. Node.js version should be >6.4.0+ Then, to use Webpacker with Elm,
rails new elm-on-rails --webpack=elm
If Rails application is already setup with webpacker, run
./bin/rails webpacker:install:elm
within the app. If it’s not setup with webpacker, add gem ‘webpacker’ to your gemfile and then install Elm. Navigate to app/javascript/packs directory.  A file named Main.elm that displays “Hello Elm!” text is generated there, along with a companion file hello_elm.js
#hello_elm.js
import Elm from './Main'
document.addEventListener('DOMContentLoaded', () => {
const target = document.createElement('div')
document.body.appendChild(target)
Elm.Main.embed(target)
})
Now, we need a page to display the Elm output. We’ll create a new file app/views/application/index.html.erb in order to render the layout: Use the javascript_pack_tag helper to import hello_elm.js file:
<%= javascript_pack_tag "hello_elm" %>
Add this line in application.html.erb or index.html.erb since hello_elm.js file embeds the output in the current document. Then add a default route
#config/routes.rb
get '/', to: 'application#index'
Before firing up Rails server, do
bundle exec rails webpacker:compile
Then run rails server and you will be seeing the “Hello Elm!” greeting.

Immutable Data and Pure Functions

Elm programs are made up of immutable data and pure functions. This contributes to the simplicity of the language. In Ruby, if I say,
user.update(params)
Calling an update method will change that user object in place. The initial value in user object is gone now if we do not have a copy of it before method call. In the same controller,
new_hash = hash.merge(params)
Calling hash.merge doesn’t modify the hash in place but returns a new_hash with updates applied to it. The above examples are two different APIs designed in different ways. This can be confusing to beginners if they find it difficult to understand why different functions work in different ways. But it’s not an issue in Elm as its values are immutable. In Elm,
newUser = update params user
user object can be passed to update function but that can’t change its value; it’s immutable. What we can do is to create a new value with some changes applied to it. Thus, the update function will return the newly updated user. That’s just how all Elm functions work, so there’s little situation of confusion as explained first. The update function we explained in Ruby can have side effects. It brings changes to the database. If a function does nothing but provides the same return value based on its arguments, then only it can be called side-effect-free. The function should not make any impact on anything else; that’s pure function in terms of functional programming. Elm functions cannot have side effects because all Elm functions are pure functions – a strong reason why Elm defined as a functional programming language. The only thing the functions do in Elm is calculating its return value by the arguments that we passed to and constants. They cannot have side effects. The advantage is that we get more predictable programs.

Elm Platform

To develop our Elm applications, four tools will be essential.
  • elm-repl
To quickly execute a small piece of Elm code from the command line without having to create a file. To start using the REPL, execute elm-repl in the terminal.
  • elm-reactor
Creates a server that will compile your elm code and will be able to see the result directly on the browser. Also, we can set a different port and custom address.
elm-reactor -a 0.0.0.0 -p 3000
  • elm-make
We can use elm-make to translate the Elm code to native files for the web(HTML, CSS, and JavaScript)
  • elm-package
Tool to install and publish Elm packages. Type elm-package in your command line to make it available, and to install a package:
elm-package install <package-name>
Elm having a solid architecture enables to build our projects with a guarantee that we have things under control even when application increases in complexity.  

References

 ]]>