Elm is a functional programming language that compiles to JavaScript. It is extremely fast and has great features such as immutability, static typing and module system. It was designed as part of Evan Czaplicki's thesis in 2012.
After playing around with Elm for the last couple of months I conclude that Elm is a very thought-out language and it has all the features a modern programming language should have. Let us look into the language more in detail.
Installation
If you are on Mac OSX, installation is pretty straightforward with brew. On linux, the installation is possible via NPM. If on Windows, God help you. Just kidding, there is a installer that you should be able to find quite easily.
Vim Plugin
I use Vim and if you do too then take a look at elm.vim Plugin.
Type Annotations
Since Elm is statically typed language it infers the Type annotations. You can optionally declare types and in the long run it will make your life very easy. Elm comes with a REPL and let us fire it off. You should see something similar to this.
elm repl
---- elm repl 0.16.0 -----------------------------------------------------------
:help for help, :exit to exit, more at
--------------------------------------------------------------------------------
> 365
365 : number
> 3.1415
3.1415 : Float
> "hi there!"
"hi there!" : String
> False
False : Bool
-- import the String Module
> import String
> String.repeat
: Int -> String -> String
-- To exit the REPL
:exit
In Elm the last argument is always the return value which in the case above is String and it accepts 2 arguments first one is Int and second one is String. The Type annotation will you out tremendously, especially if you have a large application and no more stack traces. I mean it, try it for yourself.
> String.repeat "2" "Namaste!"
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm
The 1st argument to function `repeat` is causing a mismatch.
4│ String.repeat "2" "Namaste!"
^^^
Function `repeat` is expecting the 1st argument to be:
Int
But it is:
String
Isn't that a very to-the point error message? Take that JAVA! Before we can write our todo app, we need to understand a couple of concepts. viz. Signal and Mailboxes.
Signals
Signals are one of the building blocks for creating applications in Elm. Signal is a stream of values that change over time. Signals can be merged, transformed and filtered. The best way to learn is to see this in action. Save the following snippet as Key.elm and compile it with elm-make Key.elm. Elm compiler will create an index.html. You have an option to create a JavaScript file too which we'll see later.
module Key where
import Graphics.Element exposing (..)
import Keyboard
import Char
main: Signal Element
main =
Signal.map show Keyboard.presses
In elm, you can create a namespace with the module Name where syntax. The elm script starts off with the main function. Take note of the type annotation for main, it returns a Signal Element. The Signal module has a function called map and if you try to find the type annotation for it you will see
<function: map> : (a -> b) -> Signal.Signal a -> Signal.Signal b
If you have been introduced to functional programming this will be very familiar. Map takes an operation and performs it on the Signal b and returns a Signal. In our case we are taking the Signal from the keyboard presses and performing the show which comes from Graphics.Element module. Signal.Signal can be a bit confusing but it means the Signal module has a type called Signal. If you open index.html in the browser you can see that it will display the ascii code for whatever key you press on the keyboard. This is great! Let us improve on this and display the actual character.
module Key where
import Graphics.Element exposing (..)
import Keyboard
import Char
main: Signal Element
main =
Signal.map display Keyboard.presses
display: Int -> Element
display key =
show ( Char.fromCode key )
We define a function called display that takes one argument called key which is Int and returns the Element from Graphics.Element module. Compile and open it in the browser you will see the associated character being displayed.
Let us build an HTML page that will store the keystrokes and save it and display all the characters displayed. We will need the Html module which takes 2 arguments which are both lists. The first one is a list of attributes and the second is the list of text. You will need to install the elm-html module and you can do it like so
elm package install evancz/elm-html
Let us start with a simple app that just displays an HTML page with a message wrapped in a h1 tag and a character wrapped in a h2 tag.
module KeyPress where
import Html exposing (Html, text, div, h1, h2)
import Keyboard
import Char
type alias Model = Char
model: Model
model = 'a'
view: Model -> Html
view model =
div []
[
h1 [] [ text "Begin typing..." ],
h2 [] [ text (toString model) ]
]
main: Html
main =
view model
We just created an alias for Char and called it Model. If you have been following along then the rest seems very straightforward. Let us introduce some Signals displaying the last keypress.
module KeyPress as
import Html exposing (Html, text, div, h1, h2)
import Keyboard
import Char
import String
type alias Model = Char
model: Model
model = ' '
update: Signal Model
update =
Signal.foldp ( \key model -> Char.fromCode(key) ) model Keyboard.presses
view: Model -> Html
view model =
div []
[
h1 [] [ text "Begin typing..." ],
h2 [] [ text (toString model) ]
]
main: Signal Html
main =
Signal.map view update
Before we understand this we need to understand folding, let us start with understanding folding on a list. foldl or foldr is similar to reduce operation in functional programming. foldl take a transformation operation, an aggregator and lastly a list and returns a result. In Elm, the way you create an anonymous function is by putting a backslash in parenthesis. Similary we can create do folding from the past for a Signal using foldp. This is very powerful and can be used to store state.
<function: foldl> : (a -> b -> b) -> b -> List a -> b
List.foldl (\number sum -> sum + number) 0 [1, 2, 3, 4, 5]
Going back to the previous code, we are simply creating a past dependent Signal by taking the key presses and storing into our model. Compile it and you will see that we are able to capture all the keystokes and display it. In the finally iteration, we'll convert our model to a list of characters and display all them.
module KeyPress where
import Html exposing (Html, text, div, h1, h2)
import Keyboard
import Char
-- MODEL
type alias Model =
{ chars : List Char }
initialModel: Model
initialModel =
{ chars = [] }
-- UPDATE
update: Char.KeyCode -> Model -> Model
update key model = {
model | chars = model.chars ++ [(Char.fromCode key)]
}
input: Signal Model
input =
Signal.foldp update initialModel Keyboard.presses
-- VIEW
view : Model -> Html
view model =
div
[]
[
h1 [] [ text "Begin typing..." ],
h2 [] [ text (String.fromList model.chars) ],
div [] [ text ("Debug: " ++ (toString model)) ]
]
main : Signal Html
main =
Signal.map view input
Model Update View Architecture
I have introduced some comments as Model, Update and View to highlight an architecture that you'll see repeatedly in Elm. All Elm programs follow this architecture. A stream of events are converted into actions, which then calculates the new state (new state of our model since it is immutable) and renders the View with the updated state.
We covered a lot today, in the next part we'll cover Mailboxes and finally in the last part we'll build a Todo web app with all the knowledge we have gained.