Posted by Gizra on Thu, 03/24/2016 - 00:00

We've reached the point we needed to translate one of our Elm apps to multiple languages.

View demo
Get the source code

We looked for existing solutions and stumbled upon the elm-i18n library. This
of course made us happy, to see that someone has already solved the problem for us. However, when we looked at the example something felt missing. Type safety.

In Gizra we deal most hours of our working days with PHP, so you can say we are
fearless developers. That is, we hope and believe our code is right, and
worst case we know we will catch our bugs on run time.

But Elm can make us better developers, and give us some safety!

Continue reading…

Posted by Gizra on Thu, 03/24/2016 - 00:00

The 2nd Elm - TLV meetup was lots of fun. But it was in Hebrew, and the intersection
between Hebrew speakers and Elm devs is still very limited.

So I've re-recorded my presentation in English. If you haven't ever seen Elm, or
already develop in Elm and want to better understand "how to think Elm" this presentation
might give you a nudge in the right direction.

Continue reading…

Posted by codecentric on Mon, 03/21/2016 - 11:30

Elm Friday: Union Types
In the last episode we took a look at some of the type constructs Elm provides, namely type aliases and records. We continue in this episode by looking at the last major type construct, union types.

About This Series
This is the tenth post in a series of short and sweet blog posts about Elm. The stated goal of this series is to take you from “completely clueless about Elm” to “chief Elm guru”, step by step. If you have missed the previous episodes, you might want to check out the table of contents.
Union Types
Union types are similar to enumerations, which you might now from other languages. A simple union type could look like this:

type Fruit = Apple | Banana | Orange

This declares a new union type that has three possible values (Apple, Orange and Banana in this example).
Union types and case statements are a perfect match, so this is a good time to introduce the case statement:

module Main (..) where

import Html exposing (..)

type Fruit
= Apple
| Banana
| Orange

fruitToProverb : Fruit -> String
fruitToProverb fruit =
case fruit of
Apple -> "An apple a day keeps the doctor away"
Banana -> "Time flies like an arrow; fruit flies like a banana."
Orange -> "An orange never bears a lime."

main : Html
main =
[ li [] [ Apple |> fruitToProverb |> text ]
, li [] [ Banana |> fruitToProverb |> text ]
, li [] [ Orange |> fruitToProverb |> text ]

The case-of statement in the fruitToProverb function takes an expression (in this case the fruit parameter) and matches it against all listed cases. The branch that matches will be used. So, if you pass the value Apple into the function it will return the string "An apple a day keeps the doctor away".
Tagged Unions
Union Types can actually be more than simple enumerations – they can carry additional data. This is achieved with tagged unions. The following example defines a union type Shape with two possible shapes:

-- A record definition. We had a look at records in the previous episode.
type alias Point =
{ x : Float
, y : Float

-- the tagged union Shape
type Shape
= Circle Point Float
| Rectangle Point Point

Each kind of Shape defines different data that can be attached to it. A Circle can hold a Point (its center) and its radius (represented as a Float). The Rectangle in contrast has two Points attached to it, representing the upper left and the lower right corner. Keep in mind that the number of parameters and their types can be different for each member of a tagged union. You can also mix tagged union types with simple values (that do not have additional data).
Here is an example where you can see a tagged union in action:

import Html exposing (..)

type alias Point =
{ x : Float
, y : Float

type Shape
= Circle Point Float
| Rectangle Point Point

area : Shape -> Float
area shape =
case shape of
Circle center radius ->
pi * radius ^ 2

Rectangle corner1 corner2 ->
abs (corner1.x - corner2.x) * abs (corner1.y - corner2.y)

main : Html
main =
circle =
Circle { x = 2.3, y = 1.4 } 3.1

rectangle =
Rectangle { x = 0.5, y = 1.4 } { x = 3.5, y = 5.2 }
[ li [] [ area circle |> toString |> text ]
, li [] [ area rectangle |> toString |> text ]

The interesting part is the area function. We use a case statement to calculate the area of a geometrical shape, which requires a different formula depending on the kind of shape. We also use the case statement to destructure the tagged union values, that is, to assign an identifier to the values inside the tagged union members. For example, the line
Circle center radius ->
enables us to access the Float attached to the Circle and use it in our calculation by assigning the identifier radius to it. Since we do not use the center value at all (it is not relevant for calculating the area), we also use the underscore wildcard (_) which basically translates to "ignore this value, we do not need it here":
Circle _ radius ->
That’s about it on union types and tagged unions, so this concludes the tenth episode of this blog post series on Elm. Stay tuned for the next episode!
The post Elm Friday: Type System Basics – Union Types and Tagged Unions (Part X) appeared first on codecentric Blog.

Posted by Rundis on Mon, 03/14/2016 - 01:00

The elm-light plugin provides a pretty useful
featureset for developing elm applications. Until now all features have been implemented
using a combination of ClojureScript and JavaScript. But wouldn’t it be cool if the plugin
implemented Elm features using Elm where that’s feasible ? Elm compiles to JavaScript and
JavaScript interop in ClojureScript is quite easy so it shouldn’t be that hard really.

If nothing else I thought it would be a fun challenge, so I set forth and decided to implemented
a simple module browser for Elm projects.

Elm for the UI

In Elm it’s recommended that you follow The Elm Architecture (AKA: TEA).
You model your Elm application and components into 3 separate parts; Model, View and Update.
The easiest way to get started with implementing something following TEA is using the start-app package.


Quite often you’ll find that you start by thinking about how to design your model.
This was also the case for me when developing the module browser.

type alias Model = (1)
{ allModules : List Modul
, filteredModules : List Modul
, searchStr : String
, selected : Maybe Modul

type alias Modul = (2)
{ name : String
, file : String
, packageName : String
, version : String

The model is quite simple and contains; a list of all modules, the currently filtered modules, the search string entered by the user and the currently selected module

Since Module is a reserved word in Elm the type used for representing a project Module is doofily named Modul.

For more info about what Elm modules are check out the elm-guides


Update is where we actually implement the logic of our Elm application. I won’t cover
all the details, but let’s walk through the most important bits.

type Action (1)
= NoOp
| Filter String
| Prev
| Next
| Select
| ClickSelect String
| Close
| Refresh (List Modul)

update : Action -> Model -> ( Model, Effects Action ) (2)
update action model =
case action of
NoOp -> (3)
( model, Effects.none )

Filter str -> (4)
filtered =
filterModules str model.allModules

sel =
List.head filtered
( { model
| searchStr = str
, filteredModules = filtered
, selected = sel
, Effects.none

Prev -> (5)
( { model | selected = prevModule model }
, notifyChangeSelection

Next ->
( { model | selected = nextModule model }
, notifyChangeSelection

Select -> (6)
case model.selected of
Nothing ->
( model, Effects.none )

Just x ->
( model
, notifySelect x.file

ClickSelect file -> (7)
( model
, notifySelect file

Close -> (8)
( model, notifyClose )

Refresh modules -> (9)
( Model modules modules "" (List.head modules)
, Effects.none

The actions that causes changes to the model is represented by a Union Type called Action.
If you’re not sure what union type means, think of it as a Enum on stereoids.

The update function takes an action and the current model as parameters and returns
a tuple of an (possibly) updated model and an Effect. Effects are basically things that have side-effects (http/ajax, interacting with the browser etc).
We treat an effect like a value in the application, the Elm runtime takes care of actually executing it.

NoOp is just that. It’s handy when initializing the app and also for mapping effects to when there are
effects that we don’t care about in the context of this update function

Whenever the user changes the search string input the Filter action is called. It uses a filterModules helper function
to filter modules with names starting with the given search string. We default the selected
module to the first in the filtered results. The model is NOT mutated, rather we return a new updated model.
Elm keeps track of our global model state !

Prev and Next selects/highlights the next/previous module given the currently selected one.
The notifyChangeSelection function call results in an effect that allows us to communicate with the ClojureScript part
of the module browser feature. We’ll get back to that further on.

The Select action is triggered when the users presses Enter. It selects the module and should
ultimately result in opening the Elm Module file. Again to make that happen we need to communicate
with our ClojureScript backend. This is achived through the notifySelect helper function.

ClickSelect is similar to Select but handles when the user uses the mouse to select a module.

Close - When the user presses the escape key, the module browser should close. Again we
need to notify the ClojureScript backend

To populate the Module browser ui with modules the Refresh action is called. This action
is actually triggered by our ClojureScript backend.

Before we dive into more details about the interop with ClojureScript, let’s quickly go through
the view rendering logic.


The view part in Elm is also entirely functional and you as an application developer
never touches the DOM directly. Given the current Model you tell Elm what the view should look
like, and Elm (through the use of Virtual DOM) takes care of efficiently
updating the DOM for you.

The view for the module browser is really quite simple and consist of a search input field
and an ul for listing the modules.


view : Signal.Address Action -> Model -> Html (1)
view address model =
[ class "filter-list" ] (2)
[ searchInputView address model
, ul
( (\m -> itemView address m model) model.filteredModules) (3)

searchInputView : Signal.Address Action -> Model -> Html (4)
searchInputView address model =
options =
{ preventDefault = True, stopPropagation = False }

keyActions =
Dict.fromList [ ( 38, Prev ), ( 40, Next ), ( 13, Select ), ( 27, Close ) ] (5)

dec =
(Json.customDecoder (6)
(\k ->
if Dict.member k keyActions then
Ok k
Err "not handling that key"

handleKeydown k = (7)
Maybe.withDefault NoOp (Dict.get k keyActions) |> Signal.message address
input (8)
[ value model.searchStr
, class "search"
, type' "text"
, placeholder "search"
, on "input" targetValue (\str -> Signal.message address (Filter str))
, onWithOptions "keydown" options dec handleKeydown

itemView : Signal.Address Action -> Modul -> Model -> Html
itemView address mod model = (9)
pipeM = (10)
flip Maybe.andThen

itemClass = (11)
|> pipeM
(\sel ->
if (sel == mod) then
Just "selected"
|> Maybe.withDefault ""
[ class itemClass
, onClick address (ClickSelect mod.file)
[ p [] [ text ]
, p [ class "binding" ] [ text (mod.packageName ++ " - " ++ mod.version) ]

The main view function takes an Address and the current Model as input and returns
a virtual HTML that represents the UI we want rendered. In Elm we use something called mailboxes
to respond to user interactions. Check out the note section below for more details if you’re interested.
In short the address param is the address to a given mailbox. Elm picks up any messages in the mailbox, handles them
and ultimately the results flow back to our application through the previously described update function.

All HTML tags have a corresponding function and all follow the same pattern. The first argument is a list of attributes,
the second is a list of sub elements.

The beauty of everything being a function (as opposed to templating languages) is that you have the full power of the language
to construct your view. Map, filter, reduce etc to your heart’s content.

The searchInputView function renders the search input field. This is where most of the user interaction stuff happens
so it’s naturally the most complex part of the UI.

We use the Dict type to represent key/values. Think map if you’re from a Clojure background! The keyActions
map lists the keycode and update action combo we are interested in handling.

We want to intercept just the given keyCodes everything else should flow through and update the searchStr in our model.
To support that we need to implement a custom decoder for the keydown event.

You can read handleKeydown as follows, if the keyCode lookup for the given k returns an Action use that
otherwise use the default NoOp action. The result from that is used as the last param of the Signal.message function.
(In Clojure terms you can think of |> as thread-last). Signal.message sends the given action to the given address.

The search input handles changes to the input by triggering the Filter action with a payload
which is the current value of the input. To handle the special characters we handle the keydown event using
the local helper function we outlined in <7>.

itemView constructs the view for each individual item. Most of the logic here is related to giving the
currently selected item it’s own css class.

Maybe.andThen is a function to help you chain maybes.
(There is no such thing as null/nil in Elm !). flip flips the order of the two first arguments, and we do it to allow us to chain calls using the |> operator

If an item is selected and the selected item is the same as the current module being rendered then the class should be selected in all other cases
the class is an empty string.

To understand more about Mailboxes, Addresses and the term Signal in Elm. You might want
to check out the relevant Elm docs or maybe this nice blog post

Interop with ClojureScript using Ports

Interop with JavaScript in Elm goes through strict boundaries and use a mechanism called ports.
The strict boundary is in place to ensure that you can’t get runtime exceptions in Elm (due to nulls, undefined is not a function, type mismatches etc etc).
At first it feels a little bit cumbersome, but really the guarantees given from Elm makes up for it in the long run. Big time.

The following blog post really helped me out when doing the ports stuff; "Ports in Elm"

-- Inbound

modzSignal : Signal Action (1)
modzSignal = Refresh modzPort

port modzPort : Signal (List Modul) (2)

-- Outbound

selectMailbox : Signal.Mailbox String (3)
selectMailbox =
Signal.mailbox ""

port select : Signal String (4)
port select =

changeSelectionMailbox : Signal.Mailbox () (5)
changeSelectionMailbox =
Signal.mailbox ()

port changeSelection : Signal () (6)
port changeSelection =

closeMailbox : Signal.Mailbox ()
closeMailbox =
Signal.mailbox ()

port close : Signal ()
port close =

Signals are basically values that changes over time. A signal always has a value.
If you remember our update function, it takes an Action as the first argument. To allow
our incoming module list to trigger an update we need to convert the value we receive from the
modzPort to a Refresh action (with a payload which is a List of Modul records)

modzPort is a port which is a Signal that receives values from outside of Elm. Typically JavaScript
or in our instance ClojureScript. A Signal always has a value, so you will see that we need to provide an initial value
when we start the elm app from ClojureScript later on.

When using the Elm start app package we typically use mailboxes to
achieve (side-) effects. So to send messages to JavaScript (or ClojureScript!) we create an intermediary mailbox
to communicate through an outgoing port. When we select a module in the module browser we send the file name of the module
we wish to open and the type of the file name is String. Hence the Mailbox is a mailbox for string messages.

The select port is a Signal of Strings (file names) that we can subscribe to from JavaScript(/ClojureScript).
You can think of it as an Observable (in RxJs terms) or maybe simpler an event emitter if you like.

() in Elm means the same as void or no value.

When the user changes which module is selected/hightlighted we don’t care about the value, in this instance we just need to know that the user changed their selection

Wiring up Elm with Start app

app : StartApp.App Model (1)
app =
{ init = init
, update = update
, view = view
, inputs = [ modzSignal ] (2)

main : Signal Html (3)
main =

port tasks : Signal (Task.Task Never ()) (4)
port tasks =

StartApp.start takes care of wiring up our Elm application. init creates an initial empty Model, the other functions
we have already described.

StartApp also takes an inputs argument, here we need to remember to add our modzSignal so that it
is picked up and handled by StartApp.

main is the entry point for any Elm application.

Elm executes side effects through something called tasks
I won’t go into details here, but just remember to add this incantation when using StartApp.

Wrapping up the Elm part

Right so that wes pretty much all there is to the Elm part. Of course we also need to remember to compile
the Elm code to JavaScript before we can use it from Light Table.
To do that we use the elm-make executable that comes with the elm-platform installation

I can assure you that I didn’t get a single run time exception whilst developing the Elm part. It did
get lots of helpful compiler errors along the way, but as soon as the compiler was happy the Elm application ran just as expected.
It’s hard to describe the experience, but trust me, it’s certainly worth a try !
To be able to easily test and get visual feedback along the way I set up a dummy html page.

Ok let’s move on to the ClojureScript part were we hook the ui up to the Light Table plugin.

ClojureScript and Light Table

Generating the list of Elm Modules

Unfortunately there isn’t any API AFAIK that provides the information I wished to present
(ideally all modules and for each module, all it’s publicly exposed functions/types/values).
So I had to go down a route where I use a combination of the elm project file (elm-package.json) and
artifacts (files) generated when you run elm-make on your elm project.

(defn- resolve-module-file [project-path pck-json package module version] (1)
(->> pck-json
(map #(files/join project-path
(str (s/replace module "." files/separator) ".elm")))
(some #(if (files/exists? %) % nil))))

(defn- get-exposed-modules [project-path {:keys [package exact]}] (2)
(let [pck-json (u/parse-json-file (files/join project-path
package exact
(->> pck-json
(map (fn [x]
{:name x
:packageName package
:version exact
:file (resolve-module-file project-path pck-json package x exact)})))))

(defn- get-package-modules [project-path] (3)
(->> (u/get-project-deps project-path)
(filter :exact)
(mapcat (partial get-exposed-modules project-path))
(sort-by :name)))

(defn- deduce-module-name [root-path elm-file-path] (4)
(-> elm-file-path
(s/replace root-path "")
(s/replace ".elm" "")
(s/replace #"^/" "")
(s/replace files/separator ".")))

(defn- get-project-modules [project-path] (5)
(let [pck-json (u/parse-json-file (files/join project-path "elm-package.json"))]
(->> (:source-directories pck-json)
(mapcat (fn [dir]
(if (= dir ".")
(->> (files/ls project-path) ;; fixme: no nesting allowed to avoid elm-stuff etc
(filter #(= (files/ext %) "elm"))
(map (fn [x]
{:name (deduce-module-name "" x)
:file (files/join project-path x)})))
(->> (files/filter-walk #(= (files/ext %) "elm") (files/join project-path dir))
(map (fn [x]
{:name (deduce-module-name (files/join project-path dir) x)
:file x}))))))
(map (fn [m]
(assoc m :packageName (files/basename project-path) :version (:version pck-json))))
(sort-by :name))))

(defn get-all-modules [project-path] (6)
(get-project-modules project-path)
(get-package-modules project-path)))

Helper function which tries to resolve the file for a Module from a 3rd party library

Every 3rd party library also comes with a elm-package.json that lists which module are
publicly exposed. This helper function generates module info for all exposed modules from a 3rd party library

Given all defined project dependencies for a project at a given project-path this function generates
module informaation for all this packages. It will only try to resolve modules which has a resolved version :exact, so there is a precondition
that you have run either elm-package install or elm-make successfully on your project first.

deduce-module-name is a helper function which tries to deduce the module name for an Elm file in your project

Helper function that takes a simplistic approach to try to find all modules in you project and generate module information for them
It uses the "source-directories" key in your project’s elm-package.json as a starting point.

The complete list of modules is a concatination of 3rd party modules and your project modules.

There are a few simplifications in this implementation that might yield incomplete results (and sometimes erronous).
However for the majority of cases it should work fine.

Light Table sidebar

The module browser will live in the right sidebar in Light Table. The following code will
construct the wrapper view and a Light Table object that will allow us to wire up the appropriate

(defui wrapper [this] (1)
[:div {:id "elm-module-browser"} "Retrieving modules..."])

(object/object* ::modulebrowser (2)
:tags #{:elm.modulebrowser}
:label "Elm module browser"
:order 2
:init (fn [this]
(wrapper this)))

(def module-bar (object/create ::modulebrowser)) (3)

(sidebar/add-item sidebar/rightbar module-bar) (4)

Helper function to create a wrapper div which will host our module browser

A Light Table object (basically an ClojureScript atom) that allows us to tag behaviors.

The object above is instantiated at start up

We add the module bar to the right hand sidebar in Light Table

Light Table behaviors

(behavior ::clear! (1)
:triggers #{:clear!}
:reaction (fn [this]
(cmd/exec! :close-sidebar)))

(behavior ::focus! (2)
:triggers #{:focus!}
:reaction (fn [this]
(let [input (dom/$ "#elm-module-browser input")]
(.focus input))))

(behavior ::ensure-visible (3)
:triggers #{:ensure-visible}
:reaction (fn [this]
(sidebar-cmd/ensure-visible this)))

(behavior ::show-project-modules (4)
:triggers #{:show-project-modules}
:reaction (fn [this prj-path]
(let [modules (get-all-modules prj-path)
el (dom/$ "#elm-module-browser")
mod-browser (.embed js/Elm js/Elm.ModuleBrowser el (clj->js {:modzPort []}))] (5)

(.send (.-modzPort (.-ports mod-browser)) (clj->js modules)) (6)

;; set up port subscriptions

(.subscribe (.-changeSelection (.-ports mod-browser)) (7)
(fn []
(object/raise this :ensure-visible)))

(.subscribe (.-select (.-ports mod-browser))
(fn [file]
(cmd/exec! :open-path file)
(object/raise this :clear!)))

(.subscribe (.-close (.-ports mod-browser))
(fn []
(object/raise this :clear!)))

(object/raise this :focus!))))

(behavior ::list-modules (8)
:triggers #{:editor.elm.list-modules}
:reaction (fn [ed]
(when-let [prj-path (u/project-path (-> @ed :info :path))]
(object/raise sidebar/rightbar :toggle module-bar)
(object/raise module-bar :show-project-modules prj-path)))))

(cmd/command {:command :show-modulebrowser (9)
:desc "Elm: Show module-browser"
:exec (fn []
(when-let [ed (pool/last-active)]
(object/raise ed :editor.elm.list-modules)))})

This behavior basically closes the module browser sidebar when triggered

We need to be able to set focus to the search input field when we open the module browser

Helper behavior that ensures that the currently selected item in the module browser is visible
on the screen. Ie it will scroll the div contents accordingly using a LT core helper function.

This is were we hook everything up. We gather the module information for the given project
instantiate the Elm app, subscribe to outgoing messages(/signals!) and populate the module browser
with the module list.

We start the elm app here and tells it to render in the wrapper div defined previously. We provide
an initial value for the modzPort with an empty list. (Could have provided the gathered list modules here, but wanted to show how you send messages to a inbound Elm port explicitly. See next step)

To populate the module browser we send a message to the modzPort. Elm port thinks in JavaScript so we need to convert our list of ClojureScript maps to a list of JavaScript objects

To listen to events from the Elm app we call subscribe with a given callback function. In this example
we trigger the ensure-visible behavior when the users moves the selection up or down, to ensure the selected item stays visible.

The behaviors above was tied(tagged) to the module-bar object, however this behavior is tagged to
a currently opened and active elm editor object. Light Table has no concept of projects, so to deduce which project we should
open the module browser for we need a starting point. Any elm file in your project will do. Based on that
we can deduce the root project path. If we find a project we display the module bar view and trigger the behavior
for populating the module browser.

Commands are the user interfacing functions that responds to user actions. They can be listed in the command bar in Light Table
and you can assign shortcuts to them. The show-modulebrowser command triggers the list-modules behavior.
Commands are available regardless of which editor you trigger them from, this is why we introduced the intermediary 'list-modules` behavior
because that allows us to declaritly filter when this behavior will be triggered. You’ll see how when we describe behaviors wiring in Light Table.

Wiring up LT behaviors

In our plugin behaviors file we need to wire up our behaviors.

[:editor.elm :lt.plugins.elm-light.modulebrowser/list-modules] (1)
[:elm.modulebrowser :lt.plugins.elm-light.modulebrowser/clear!] (2)
[:elm.modulebrowser :lt.plugins.elm-light.modulebrowser/show-project-modules]
[:elm.modulebrowser :lt.plugins.elm-light.modulebrowser/focus!]
[:elm.modulebrowser :lt.plugins.elm-light.modulebrowser/ensure-visible]

Here we tell Light Table that only editor objects with the tag :editor.elm
will respond with the list-modules behavior we described earlier

Similaritly the other behaviors will only be triggerd by objects tagged with :elm-modulebrowser.
In our case that would be the module-bar object we defined.

Why all this ceremony with behaviors ?
Flexibility! It allows us to easily turn on/off features while Light Table is running. If you wish you could quite easily
create your own implementation for a behavior and replace the one supplied by the plugin.
Or maybe you’d like to do something in addition for a given behavior trigger.


Okay let’s be honest. We haven’t set the world alight with a killer feature that couldn’t
be accomplished quite easily without Elm. Neither have we created an advanced demo for Elm and ClojureScript integration.
But we’ve certainly proven that it’s possible and it wasn’t particularily difficult.
It somehow feels better with an Elm plugin that has Elm as part of it’s implementation.

You can do some pretty awesomly advanced UI’s with Elm and combing it with ClojureScript is definitely feasible.
I’ll leave it to you to evaluate if that would ever make sense to do though !

Posted by Rundis on Tue, 03/01/2016 - 01:00

So the hypothesis from episode 3 was that it should
be relatively easy to add new features. In this episode we’ll put that hypothesis to the test and add CRUD features
for Albums. There will be a little refactoring, no testing, premature optimizations and plenty of "let the friendly Elm and Haskell compilers guide us along the way".

Useful resources

  • Check out the other episodes in this blog series.
  • The accompanying Albums sample app is on github, and there is a tag
    for each episode

Table of Contents


When I set out to implement the features for this episode I didn’t really reflect on how I would then later
go about blogging about it. It turns out I probably did way to many changes to fit nicely into a blog episode.
Let’s just say I got caught up in a coding frenzy, but let me assure you I had a blast coding for this episode !
This means I wont be going into detail about every change I’ve made since the last episode, but rather try to highlight
the most important/interesting ones.

A highlevel summary of changes includes:

  • Haskell stack has been introduced to the backend
  • Implemented REST endpoints for Albums CRUD
    • Backend now composes endpoints for Artists and Albums
    • Data model changed to account for Album and Track entities
    • Bootstrapping of sample data extended and refactored to a separate module
  • Implemented UI for listing, deleting, creating, updating and displaying album details
    • In particular the the features for creating/updating Albums and associated tracks, gives a glimpse
      of the compasability powers of the Elm Architecture



Working with Cabal and Cabal sandboxes is a bit of a pain. Stack promises to alleviate some of those pains, so I figured
I’d give it a go. There are probably tutorials/blog posts out there going into how you should go about migrating
to use stack in your Haskell projects, so I won’t go into any details here.
Basically I installed stack and added a stack configuration file stack.yml. After that I was pretty much up and running.
The instructions for running the sample app with stack can be found in the Albums README.


albums db part4

The datamodel contains a little bit of flexibility so that a track can be potentially be included in many albums
(hence the album_track entity). For this episode though, we’re not using that and of course that innocent bit of flexibility
comes with a cost of added complexity. I considered removing the album_track entity, but decided against it. I figured
that in a real project this is a typical example of things you have to deal with (say you have a DBA or even more relevant…​ and exisiting datamodel you have to live with).
Let’s run with it, and try to deal with it along the way.


The code for schema creation and bootstrapping test data has been moved to a separate module.


bootstrapDB :: Sql.Connection -> IO ()
bootstrapDB conn = do
createSchema conn
populateSampleData conn

createSchema :: Sql.Connection -> IO ()
createSchema conn = do
executeDB "PRAGMA foreign_keys = ON"
executeDB "create table artist (id integer primary key asc, name varchar2(255))"
executeDB "create table track (id integer primary key asc, name varchar2(255), duration integer)"
executeDB "create table album (id integer primary key asc, artist_id integer, name varchar2(255), FOREIGN KEY(artist_id) references artist(id))"
executeDB "create table album_track (track_no integer, album_id, track_id, primary key(track_no, album_id, track_id), foreign key(album_id) references album(id), foreign key(track_id) references track(id))"

executeDB = Sql.execute_ conn

-- definition of sample data omitted for brevity

populateSampleData :: Sql.Connection -> IO ()
populateSampleData conn = do
mapM_ insertArtist artists
mapM_ insertTrack tracks
mapM_ insertAlbum albums
mapM_ insertAlbumTrack albumTracks

insertArtist a = Sql.execute conn "insert into artist (id, name) values (?, ?)" a
insertTrack t = Sql.execute conn "insert into track (id, name, duration) values (?, ?, ?)" t
insertAlbum a = Sql.execute conn "insert into album (id, artist_id, name) values (?, ?, ?)" a
insertAlbumTrack at = Sql.execute conn "insert into album_track (track_no, album_id, track_id) values (?, ?, ?)" at

Somewhat amusing that foreign key constraints are not turned on by default in SQLite, but hey.
What’s less amusing is that foreign key exceptions are very unspecific about which contraints are violated (:

New endpoints for Albums

Model additions


data Track = Track (1)
{ trackId :: Maybe Int
, trackName :: String
, trackDuration :: Int -- seconds
} deriving (Eq, Show, Generic)

data Album = Album (2)
{ albumId :: Maybe Int
, albumName :: String
, albumArtistId :: Int
, albumTracks :: [Track]
} deriving (Eq, Show, Generic)

Our Track type doesn’t care about the distiction between the album and album_track entities

It was tempting to add Artist as a property to the Album type, but opted for just the id of an Artist entity.
I didn’t want to be forced to return a full artist instance for every Album returned. You gotta draw the line somewhere right ?

Albums CRUD functions

In order to keep this blog post from becoming to extensive we’ve only included the functions to
list and create new albums. You can view the update, findById and delete functions in the
album sample repo

findAlbums :: Sql.Connection -> IO [M.Album] (1)
findAlbums conn = do
rows <- Sql.query_ conn (albumsQuery "") :: IO [(Int, String, Int, Int, String, Int)]
return $ Map.elems $ foldl groupAlbum Map.empty rows

findAlbumsByArtist :: Sql.Connection -> Int -> IO [M.Album] (2)
findAlbumsByArtist conn artistId = do
rows <- Sql.query conn (albumsQuery " where artist_id = ?") (Sql.Only artistId) :: IO [(Int, String, Int, Int, String, Int)]
return $ Map.elems $ foldl groupAlbum Map.empty rows

albumsQuery :: String -> SqlTypes.Query (3)
albumsQuery whereClause =
SqlTypes.Query $ Txt.pack $
"select,, a.artist_id,,, t.duration \
\ from album a inner join album_track at on = at.album_id \
\ inner join track t on at.track_id ="
++ whereClause
++ " order by, at.track_no"

groupAlbum :: Map.Map Int M.Album -> (Int, String, Int, Int, String, Int) -> Map.Map Int M.Album (4)
groupAlbum acc (albumId, albumName, artistId, trackId, trackName, trackDuration) =
case (Map.lookup albumId acc) of
Nothing -> Map.insert albumId (M.Album (Just albumId) albumName artistId [M.Track (Just trackId) trackName trackDuration]) acc
Just _ -> Map.update (\a -> Just (addTrack a (trackId, trackName, trackDuration))) albumId acc
addTrack album (trackId, trackName, trackDuration) =
album {M.albumTracks = (M.albumTracks album) ++ [M.Track (Just trackId) trackName trackDuration]}

newAlbum :: Sql.Connection -> M.Album -> IO M.Album (5)
newAlbum conn album = do
Sql.executeNamed conn "insert into album (name, artist_id) values (:name, :artistId)" [":name" := (M.albumName album), ":artistId" := (M.albumArtistId album)]
albumId <- lastInsertRowId conn
tracks <- zipWithM (\t i -> newTrack conn (i, fromIntegral albumId, (M.albumArtistId album), t)) (M.albumTracks album) [0..]

return album { M.albumId = Just $ fromIntegral albumId
, M.albumTracks = tracks

newTrack :: Sql.Connection -> (Int, Int, Int, M.Track) -> IO M.Track (6)
newTrack conn (trackNo, albumId, artistId, track) = do
Sql.executeNamed conn "insert into track (name, duration) values (:name, :duration)" [":name" := (M.trackName track), ":duration" := (M.trackDuration track)]
trackId <- lastInsertRowId conn
Sql.execute conn "insert into album_track (track_no, album_id, track_id) values (?, ?, ?)" (trackNo, albumId, trackId)

return track {M.trackId = Just $ fromIntegral trackId}

Function to list all albums

Function to list albums filtered by artist

Helper function to construct an album query with an optional where clause. The query returns a product
of albums and their tracks. Let’s just call this a performance optimization to avoid n+1 queries :-)

Since album information is repeated for each track, we need to group tracks per album. This part was a fun challenge
for a Haskell noob. I’m sure it could be done eveny more succinct, but I’m reasonably happy with the way it turned out.

This is the function to create a new album with all it’s tracks. We assume the tracks are sorted in the order they
should be persisted and uses zipWith to get a mapIndexed kind of function so that we can generate the appropriate track_no
for each album_track in the db.

Working with tracks we have to consider both the track and album_track entities in the db. As it is, the
album_track table is just overhead, but we knew that allready given the design decission taken earlier. Once we need to support
the fact that a track can be included in more that one album, we need to rethink this implementation.

Adding albums to the API


type AlbumAPI = (1)
QueryParam "artistId" Int :> Get '[JSON] [M.Album] (2)
:<|> ReqBody '[JSON] M.Album :> Post '[JSON] M.Album
:<|> Capture "albumId" Int :> ReqBody '[JSON] M.Album :> Put '[JSON] M.Album
:<|> Capture "albumId" Int :> Get '[JSON] M.Album
:<|> Capture "albumId" Int :> Delete '[] ()

albumsServer :: Sql.Connection -> Server AlbumAPI
albumsServer conn =
getAlbums :<|> postAlbum :<|> updateAlbum :<|> getAlbum :<|> deleteAlbum

getAlbums artistId = liftIO $ case artistId of (3)
Nothing -> S.findAlbums conn
Just x -> S.findAlbumsByArtist conn x
postAlbum album = liftIO $ Sql.withTransaction conn $ S.newAlbum conn album
updateAlbum albumId album = liftIOMaybeToEither err404 $ Sql.withTransaction conn $ S.updateAlbum conn album albumId
getAlbum albumId = liftIOMaybeToEither err404 $ S.albumById conn albumId
deleteAlbum albumId = liftIO $ Sql.withTransaction conn $ S.deleteAlbum conn albumId

type API = "artists" :> ArtistAPI :<|> "albums" :> AlbumAPI (4)

combinedServer :: Sql.Connection -> Server API (5)
combinedServer conn = artistsServer conn :<|> albumsServer conn

We’ve added a new API type for Albums

For listing albums we support an optional query param to allow us to filter albums by artist

This implementation is quite simplistic, we probably want to provide a more generic way to handle multiple
filter criteria in the future.

The API for our backend is now a composition of the api for artists and the api for albums

As Servant allows us to compose apis it also allows us to compose servers (ie the implementations of the apis).
We create a combined server, which is what we ultimately expose from our backend server

The really observant reader might have noticed that the update function for albums is a little bit more
restrictive/solid than the corresponding function for artist. Here we actually check if the given album id
corresponds to a album in the DB. If it doesn’t we return a 404.


app :: Sql.Connection -> Application
app conn = serve A.api (A.combinedServer conn) (1)

main :: IO ()
main = do
withTestConnection $ \conn -> do
B.bootstrapDB conn (2)
run 8081 $ albumCors $ app conn

Rather than serve the just the albumServer, we now serve the combined server.

We’ve updated bootstrapping to use the the new bootstrap module

Backend summary

That wasn’t to hard now was it ? Adding additional end points was quite straightforward, the hard part
was overcoming analysis paralysis. Settling on data types and db design took some time, and in hindsight I might
have opted for a more simplistic db design. I’m also curious about how the design would have been had I started top down (frontend first)
and backend last. I have a strong suspicion it would have been different.

Haskell IO
The thing I probably spent most time struggling with was working with IO actions. Apparantly I shouldn’t
use the term IO Monad. Anyways I can’t wrap my head around
when I’m "inside" the IO thingie and when I’m not. It’s obvious that do, , let and return is something
I have to sit down and understand (in the context of IO things). My strategy of trial and error doesn’t scale
all that well, and whatsmore It feels ackward not having a clue on the reasoning on why something is working or not.
Note to self, read up on Haskell IO.

REST concerns
Even with this simple example I started to run into the same old beef I have with generic rest endpoints.
They rarely fit nicely with a Single Page Application. They work ok when it comes to adding and updating data,
but when it comes to querying it all becomes much more limiting. In a SPA you typically want much more flexibility
in terms of what you query by and what you get in return.

  • In an album listing for a given artist I might just want to display the name, release date, number of songs and album length
    I’m not interested in the tracks.
  • In an album listing / album search outside of an artist context I probably want to display the artist name
  • For a mobile client I might just want to display the album name (size of payloads might actually be important for mobile…​)
  • Likewise when listing artists I might want to display number of albums
  • Or when searching I might want to search album name, artist name and/or track name

Reading about GraphQL, Falcor
and more recently Om next has been an eye-opener to me.
The ideas here rings true and bodes well for the frontend, probably soonish something will materialize for Elm too.
But what to do on the server side I wonder ?


New routes


type Route (1)
= Home
-- ...
| AlbumDetailPage Int
| NewArtistAlbumPage Int
| EmptyRoute

routeParsers = (2)
[ static Home "/"
-- ...
, dyn1 AlbumDetailPage "/albums/" int ""
, dyn1 NewArtistAlbumPage "/artists/" int "/albums/new"

encode route = (3)
case route of
Home -> "/"
-- ...
AlbumDetailPage i -> "/albums/" ++ toString i
NewArtistAlbumPage i -> "/artists/" ++ (toString i) ++ "/albums/new"
EmptyRoute -> ""

We have added 2 new routes, one for edit/create albums, one for creating a new album (for a given artist)
(actually there is a 3 for creating an album without selecting an artist, but it’s not wired up yet)

We need to add route matchers for the new routes.

We also need to add encoders for our new routes.

Service API

To call our new REST api for albums we need to implement a few new functions and json decoders.
We’ll only show two of the api related functions.

type alias AlbumRequest a = (1)
{ a | name : String
, artistId : Int
, tracks : List Track

type alias Album = (2)
{ id : Int
, name : String
, artistId : Int
, tracks : List Track

type alias Track = (3)
{ name : String
, duration : Int

getAlbumsByArtist : Int -> (Maybe (List Album) -> a) -> Effects a (4)
getAlbumsByArtist artistId action =
Http.get albumsDecoder (baseUrl ++ "/albums?artistId=" ++ toString artistId)
|> Task.toMaybe
|> action
|> Effects.task

createAlbum : AlbumRequest a -> (Maybe Album -> b) -> Effects.Effects b (5)
createAlbum album action =
Http.send Http.defaultSettings
{ verb = "POST"
, url = baseUrl ++ "/albums"
, body = Http.string (encodeAlbum album)
, headers = [("Content-Type", "application/json")]
|> Http.fromJson albumDecoder
|> Task.toMaybe
|> action
|> Effects.task

-- other functions left out for brevity. Check out the sample code or have a look at episode 2 for inspiration

-- Decoders/encoders for albums/tracks (6)

albumsDecoder : JsonD.Decoder (List Album)
albumsDecoder =
JsonD.list albumDecoder

albumDecoder : JsonD.Decoder Album
albumDecoder =
JsonD.object4 Album
("albumId" :=
("albumName" := JsonD.string)
("albumArtistId" :=
("albumTracks" := JsonD.list trackDecoder)

trackDecoder : JsonD.Decoder Track
trackDecoder =
JsonD.object2 Track
("trackName" := JsonD.string)
("trackDuration" :=

encodeAlbum : AlbumRequest a -> String
encodeAlbum album =
JsonE.encode 0 <|
[ ("albumName", JsonE.string
, ("albumArtistId", album.artistId)
, ("albumTracks", JsonE.list <| encodeTrack album.tracks)

encodeTrack : Track -> JsonE.Value
encodeTrack track =
[ ("trackName", JsonE.string
, ("trackDuration", track.duration)

We use the AlbumRequest type when dealing with new albums

The Album type represents a persisted album

We aren’t really interested in the id of tracks so we only need one Track type

For finding albums for an artist we can use the Http.get function with default settings

To implement createAlbum we need to use Http.Send so that we can provide custom settings

Decoding/Encoding Json to/from types isn’t particularily difficult, but it is a bit of boilerplate involved

The album page

We’ve made some changes to the ArtistDetail page which we won’t show in this episode.
These changes include:

  • List all albums for an artist
  • Add features to remove album and link from each album in listin to edit the album
  • A button to initation the Album detail page in "Create New" mode


We consider an Album and it’s tracks to be an aggregate. This is also reflected in the implementation
of the ArlbumDetail module in the frontend code. You’ll hopefully see that it’s not that hard
to implement a semi advanced page by using the composability of the elm architecture.

Ok lets look at how we’ve implemented the Album detail page and it’s associated track listing.


type alias Model = (1)
{ id : Maybe Int
, artistId : Maybe Int
, name : String
, tracks : List ( TrackRowId, TrackRow.Model )
, nextTrackRowId : TrackRowId
, artists : List Artist

type alias TrackRowId = (2)

type Action (3)
= NoOp
| GetAlbum (Int)
| ShowAlbum (Maybe Album)
| HandleArtistsRetrieved (Maybe (List Artist))
| SetAlbumName (String)
| SaveAlbum
| HandleSaved (Maybe Album)
| ModifyTrack TrackRowId TrackRow.Action
| RemoveTrack TrackRowId
| MoveTrackUp TrackRowId
| MoveTrackDown TrackRowId

The model kind of reflects the Album type we saw in the previous chapter, but it’s
bespoke for use in this view. Most notably we keep a list of Artists (for an artist dropdown) and
tracks are represented as a list of trackrow models from the TrackRow.elm module.

To be able to forward updates to the appropriate TrackRow instance we are using a sequence type

There are quite a few actions, But the last 4 are related to the list of TrackRows.

AlbumDetails can be seen as holding an AlbumListing, updates that concerns the list is handled
by AlbumDetails whilst updates that concerns individual TrackRows are forwarded to the appropriate
TrackRow instance.

The update function

update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
NoOp ->
( model, Effects.none )

GetAlbum id -> (1)
( model
, Effects.batch
[ getAlbum id ShowAlbum
, getArtists HandleArtistsRetrieved

ShowAlbum maybeAlbum -> (2)
case maybeAlbum of
Just album ->
( createAlbumModel model album, Effects.none )

-- TODO: This could be an error if returned from api !
Nothing ->
( maybeAddPristine model, getArtists HandleArtistsRetrieved )

HandleArtistsRetrieved xs -> (3)
( { model | artists = (Maybe.withDefault [] xs) }
, Effects.none

SetAlbumName txt -> (4)
( { model | name = txt }
, Effects.none

SaveAlbum -> (5)
case (, model.artistId) of
(Just albumId, Just artistId) ->
( model
, updateAlbum (Album albumId artistId (createTracks model.tracks)) HandleSaved
(Nothing, Just artistId) ->
( model
, createAlbum { name =
, artistId = artistId
, tracks = (createTracks model.tracks)
} HandleSaved
(_, _) ->
Debug.crash "Missing, needs to be handled by validation"

HandleSaved maybeAlbum -> (6)
case maybeAlbum of
Just album ->
( createAlbumModel model album
, (\_ -> NoOp) (Routes.redirect <| Routes.ArtistDetailPage album.artistId)

Nothing ->
Debug.crash "Save failed... we're not handling it..."

RemoveTrack id -> (7)
( { model | tracks = List.filter (\( rowId, _ ) -> rowId /= id) model.tracks }
, Effects.none

MoveTrackUp id -> (8)
track =
ListX.find (\( rowId, _ ) -> rowId == id) model.tracks
case track of
Nothing ->
( model, Effects.none )

Just t ->
( { model | tracks = moveUp model.tracks t }
, Effects.none

MoveTrackDown id -> (9)
track =
ListX.find (\( rowId, _ ) -> rowId == id) model.tracks

mayMoveDown t =
idx =
ListX.elemIndex t model.tracks
case idx of
Nothing ->

Just i ->
i < ((List.length model.tracks) - 2)
case track of
Nothing ->
( model, Effects.none )

Just t ->
( { model
| tracks =
if (mayMoveDown t) then
moveDown model.tracks t
, Effects.none

ModifyTrack id trackRowAction -> (10)
updateTrack ( trackId, trackModel ) =
if trackId == id then
( trackId, TrackRow.update trackRowAction trackModel )
( trackId, trackModel )
( maybeAddPristine { model | tracks = updateTrack model.tracks }
, Effects.none

When we mount the route for an existing album, we need to retrieve both the album and
all artists (for the artist dropdown). To do both in one go we can use Effects.batch

We use the album param to differntiate between "update" and "new" mode for albums. If show album is called with an album we update our inital model with the information
contained in the given album (this also involves initating TrackRow.models for each album track.
If there is no album, we just add an empty track row and the initiate the retrieval of artists for the artists dropdown.

Once artists are retrieved we update our model to hold these

This action is executed when the user changes the value of the name field

The save action either calls update or create in the server api based on whether the model has an albumId or not.
In both instances it needs to convert the model to an Album/AlbumRequest as this is what the signature of the ServerApi functions require

A successful save will give an Album type back, we update the model and in this instance we
also redirect the user to the artist detail page.

This action is called when the user clicks on the remove button for a track row. We’ll get back to this when
in just a little while

Action to move a track one step up in the track listing. If it’s already at the top
it’s a no op. The "heavy" lifting is done in the moveUp generic helper function

Similar to MoveTrackUp but it has addtional logic to ensure we don’t move a track below the
always present empty (Pristine) row in the track listing

The ModifyTrack action forwards to the update function for the TrackRow in question. Each track row is
tagged with an Id (TrackRowId)

The view

view : Signal.Address Action -> Model -> Html (1)
view address model =
[ h1 [] [ text <| pageTitle model ]
, Html.form
[ class "form-horizontal" ]
[ div
[ class "form-group" ]
[ label [ class "col-sm-2 control-label" ] [ text "Name" ]
, div
[ class "col-sm-10" ]
[ input
[ class "form-control"
, value
, on "input" targetValue (\str -> Signal.message address (SetAlbumName str))
, ( artistDropDown address model )
, div
[ class "form-group" ]
[ div
[ class "col-sm-offset-2 col-sm-10" ]
[ button
[ class "btn btn-default"
, type' "button"
, onClick address SaveAlbum
[ text "Save" ]
, h2 [] [ text "Tracks" ]
, trackListing address model

artistDropDown : Signal.Address Action -> Model -> Html (2)
artistDropDown address model =
val =
Maybe.withDefault (-1) model.artistId

opt a =
option [ value <| toString, selected ( == val) ] [ text ]
[ class "form-group" ]
[ label [ class "col-sm-2 control-label" ] [ text "Artist" ]
, div
[ class "col-sm-10" ]
[ select
[ class "form-control" ]
( opt model.artists)

trackListing : Signal.Address Action -> Model -> Html (3)
trackListing address model =
[ class "table table-striped" ]
[ thead
[ tr
[ th [] []
, th [] []
, th [] [ text "Name" ]
, th [] [ text "Duration" ]
, th [] []
, tbody [] ( (trackRow address) model.tracks)

trackRow : Signal.Address Action -> ( TrackRowId, TrackRow.Model ) -> Html (4)
trackRow address ( id, rowModel ) =
context =
(Signal.forwardTo address (ModifyTrack id))
(Signal.forwardTo address (always (RemoveTrack id)))
(Signal.forwardTo address (always (MoveTrackUp id)))
(Signal.forwardTo address (always (MoveTrackDown id)))
TrackRow.view context rowModel

The view function for the page.

The artist dropdown (a github star for the observant reader that can spot what’s missing :-) )

Generates the track listing for the album

The rendering of each individual TrackRow is forwarded to the TrackRow module. We pass on a
context so that a TrackRow is able to "signal back" to the AlbumDetails page for the actions
that are owned by AlbumDetails (RemoveTrack, MoveTrackUp and MoveTrackDown). You’ll see how that
plays out when we look at the TrackRow implementation in the next secion.

Why the context thingie ?

Well we can’t have the AlbumDetails depending on TrackRows and the TrackRow component having a dependency
back to AlbumDetails. To solve that we pass on the tagged forwarding addresses so that TrackRows can signal
AlbumDetails with the appropriate actions. I guess you can sort of think of them as callbacks, but it’s not quite that.

Another slightly more elaborate explantion might be that when a user performs something on a track row that
we capture (say a click on the remove button). The view from the track row returns a signal (wrapped as an effect) to album details which
in turn returns a signal back to main. The signal is processed by the startapp "event-loop" and flows back through
the update functions (main → AlbumDetails) and since it’s tagged to as an action to be handled by AlbumDetails is handled
in AlbumDetails update function (and doesn’t flow further.

Clear as mud or perhaps it makes sort of sense ?

Track row


type alias Model = (1)
{ name : String
, durationMin : Maybe Int
, durationSec : Maybe Int
, status : Status

type alias Context = (2)
{ actions : Signal.Address Action
, remove : Signal.Address ()
, moveUp : Signal.Address ()
, moveDown : Signal.Address ()

type Status (3)
= Saved
| Modified
| Error
| Pristine (4)

type Action (5)
= SetTrackName String
| SetMinutes String
| SetSeconds String

The model captures information about an album track. Duration is separated into
minutes and seconds to be more presentable and easier for the user to input. In addition
we have a status flag to be able to give the user feedback and handle some conditional logic.

Here you see the type definition for the Context we previously mentioned we used in the when
forwarding view rendering for each individual track row in the Album Details page. (Btw it could be any
component as long as they pass on a context with the given signature of Context).

The possible status types a row can be in.

Prisitine has a special meaning in the track listing in AlbumDetails. It should always be just one and it should be the last row.
However that’s not the responsibility of TrackRow. TrackRow should just ensure the status is correct at all times.

The possible actions that TrackRow handles internally

Update function

update : Action -> Model -> Model
update action model =
case action of
SetTrackName v -> (1)
{ model | name = v, status = Modified }

SetMinutes str -> (2)
maybeMinutes = Result.toMaybe <| String.toInt str
case maybeMinutes of
Just m ->
{ model | durationMin = maybeMinutes, status = Modified }

Nothing ->
if String.isEmpty str then
{ model | durationMin = Nothing, status = Modified}

SetSeconds str -> (3)
maybeSeconds = Result.toMaybe <| String.toInt str
case maybeSeconds of
Just m ->
if m < 60 then
{ model | durationSec = maybeSeconds, status = Modified }

Nothing ->
if String.isEmpty str then
{ model | durationSec = Nothing, status = Modified}

Updates the trackname model property when user inputs into the trackname field

Updates the minutes property if a valid number is entered. Also blanks the field
when the text input field becomes empty

Similar to minutes, but also ensures that you don’t enter more than 59 !


We’ll only show parts of the view to limit the amount of code you need to scan through.

view : Context -> Model -> Html
view context model =
[ td [] [ statusView model ]
, td [] [ moveView context model ]
, td [] [ nameView context model ]
, td [] [ durationView context model ]
, td [] [ removeView context model ]

nameView : Context -> Model -> Html
nameView context model =
[ class "form-control"
, value
, on "input" targetValue (\str -> Signal.message context.actions (SetTrackName str)) (1)

removeView : Context -> Model -> Html
removeView context model =
[ onClick context.remove () (2)
, class <| "btn btn-sm btn-danger " ++ if isPristine model then "disabled" else ""
[ text "Remove" ]

When a user causes an input event on the name input field we create a message using the address in context.actions with action SetTrackName
So this message will cause an update eventually forwarded to the update function of TrackRow

When a user clicks on the remove button we use the address given by context.remove with a payload of () (ie void).
This message will always be forwarded to the address for AlbumDetails with the payload set to RemoveTrack with the given track row id.
All of which TrackRow is blissfully unaware of.

Main.elm wiring it all up

type alias Model =
{ --....
, albumDetailModel : AlbumDetail.Model

type Action
= NoOp
-- ...
| AlbumDetailAction AlbumDetail.Action
| RouterAction (TransitRouter.Action Routes.Route)

initialModel =
{ transitRouter = TransitRouter.empty Routes.EmptyRoute
-- ...
, albumDetailModel = AlbumDetail.init

mountRoute prevRoute route model =
case route of
-- ...

AlbumDetailPage albumId -> (1)
(model', effects) =
AlbumDetail.update (AlbumDetail.GetAlbum albumId) AlbumDetail.init
( { model | albumDetailModel = model' }
, AlbumDetailAction effects)

NewArtistAlbumPage artistId -> (2)
(model', effects) =
AlbumDetail.update (AlbumDetail.ShowAlbum Nothing) (AlbumDetail.initForArtist artistId)
( { model | albumDetailModel = model' }
, AlbumDetailAction effects)

-- ...

update action model =
case action of
-- ..

AlbumDetailAction act -> (3)
( model', effects ) =
AlbumDetail.update act model.albumDetailModel
( { model | albumDetailModel = model' }
, AlbumDetailAction effects

-- ..

When we mount the route for the AlbumDetailsPage ("/albums/:albumId") we call the
update function of AlbuDetail with a GetAlbum action. You might remember that this in turn calls the functions
for retrieving an Album and the function for retrieving artists as a batch.

When the user performs an action that results in the NewArtistAlbumPage being mounted ("/artists/:artistId/albums/new")
, we call the update on AlbumDetail with ShowAlbum action and a reinitialized model where artistId is set.

In the update function of Main we forward any actions particular to AlbumDetail

Frontend summary

Working with the frontend code in Elm has been mostly plain sailing. I struggled a bit to get
all my ducks(/effects) in a row and I’m not too happy with some of the interactions related to new vs update.

Unfortunately the elm-reactor isn’t working all that well with 0.16, certainly not on my machine.
It also doesn’t work particularily well with single page apps that changes the url. I looked at and tried a couple of
alternatives and settled on using elm-server. I had to make some modifications
to make it work nicely with an SPA. I submitted a PR that seems to work nicely for my use case atleast.
With that in place, the roundtrip from change to feedback became very schneizz indeed !

Undoubtably there is quite a bit that feels like boiler plate. The addition of routing also introduces yet another thing you have
to keep in mind in several places. Boilerplate it might be, but it’s also quite explicit. I would imagine that in a large app you might grow a bit weary of some of the boilerplate and start looking for ways to reduce it.

I’d be lying if I said I’ve fully grasped; signals, tasks, ports, effects and mailboxes. But it’s gradually becoming clearer
and it’s very nice that you can produce pretty cool things without investing to much up front.

Concluding remarks

I utterly failed to make a shorter blog post yet again. To my defence, the default formatting of Elm
do favor newlines bigtime. Most of the Elm code has been formatted by elm-format btw.

I’m really starting to see the benefits of statically (strongly) typed functional languages. The journey
so far has been a massive learing experience. Heck this stuff has been so much fun, I ended up taking a day off work so that
I could work on this for a whole day with most of my good brain cells still at acceptable performance levels.
Shame I can’t use this stuff at work, but I’m starting to accumulate quite a substantial collection of selling points.

Whats next ?
The sample app has started to accumulate quite a bit of technical dept, so I suppose the next episode(s)
should start to address some of that.

Posted by Trouble (::)entrating on Mon, 02/29/2016 - 03:33

I’ve just released elm-http-extra 5.0.0, and along with the links I wanted to
share the reasons for the changes and discuss some of the challenges that I
faced addressing them.

A huge thank you to Fred Yankowski for using
elm-http-extra, finding a serious flaw in the design, talking through
it with me on the Elm Slack, and
generally being an awesome member of the Elm community!

The problem

Prior to this release, Http.Extra.send accepted three arguments: a
Json.Decode.Decoder for the value expected on success, another
Json.Decode.Decoder for the value expected on server error, and a
RequestBuilder to pipe with (|>) and kick off the request. (For more
information on this API design, see Chainable APIs with the forward apply operator). This is a significant
improvement over elm-http because it lets you deal with the body of a response
in the case of a server error. You might be dealing with an API that returns an
object like the following on error:

"message": "There was an error",
"status": 500,
"success": false

With elm-http the process for accessing this data as a record is fairly
laborious, and with elm-http-extra it is easy and also required. I thought
this was a pretty clever design until Fred told me about how his API worked. On
a 404 error, his API returned the following body:

"404 page not found"

The Task failed as expected, but instead of failing with a
BadResponse String and providing access to the message, it always failed with
UnexpectedPayload and an error message "Unexpected token p”, even though
the error body was being decoded with Json.Decode.string. The problem is that
"404 page not found" is not valid JSON. "\"404 page not found\"" with the
included quotes is valid JSON. The type of value being accepted from the body
needed to be lifted one level higher.

The solution

I spent some time thinking about this and decided that the signature for send
should remain roughly the same, but include some polymorphism for how bodies are
handled. The first attempt I made was to create a tagged union which could
express either a plain text string body or a JSON body and contain a decoder:

type BodyReader a
= StringReader
| JsonReader (Json.Decode.Decoder a)

This might raise an immediate red flag in the experienced Elm programmer’s mind,
as this encoding of the transform as a union type implicitly expresses the
output type of a function on that type. Ultimately I couldn’t get a rework using
this method to compile without writing one function for each combination of
BodyReader instances for error and success cases.

So I scrapped that plan and went one step outward: generalizing the idea of what
a BodyReader does at the function level. In this case, BodyReader is just a
function interface:

type alias BodyReader a =
Http.Value -> Result String a

This interface bears a lot of resemblance to Json.Decode.decodeString, in
which a string value goes in and a Result String a comes out. Each
BodyReader is allows to take on any kind of Http.Value and optionally fail.
This has a two-fold benefit.

  1. Even though Text is the only supported Http.Value type right now, this
    generic function interface leaves room for extensibility once more values are
  2. Since we only need to write a function, readers can get even more specific
    than just accessing the string value contained by Text. jsonReader does
    this, and anyone can do the same by the output of

So there it is! Now, instead of having to pass a Json.Decode.Decoder and
always getting a parsing error, use stringReader to get a string, or continue
decoding JSON with jsonReader.

Other changes

Max Goldstein pointed out that the signature of
withHeader accepting a (String, String) added more visual noise than was
necessary. He is absolutely right, and now withHeader accepts two string, one
for the key and one for the value. See the example in the next section for a

New README example

In this example, we expect a successful response to be JSON array of strings,

["hello", "world", "this", "is", "the", "best", "json", "ever"]

and an error response might have a body which just includes text, such as the
following for a 404 error:

Not Found.

We’ll use HttpExtra.jsonReader and a Json.Decode.Decoder to parse the
successful response body and HttpExtra.stringReader to accept a string
body on error without trying to parse JSON.

import Time
import Http.Extra as HttpExtra exposing (..)
import Json.Decode as Json

itemsDecoder : Json.Decoder (List String)
itemsDecoder =
Json.list Json.string

addItem : String -> Task (HttpExtra.Error String) (HttpExtra.Response (List String))
addItem item = ""
|> withBody (Http.string "{ \"item\": \"" ++ item ++ "\" }")
|> withHeader "Content-Type" "application/json"
|> withTimeout (10 * Time.second)
|> withCredentials
|> send (jsonReader itemsDecoder) stringReader

Contributing to elm-http-extra

I’m always happy to receive any feedback and ideas about additional features or
anything at all! Any input and pull requests are very welcome and encouraged. If
you’d like to help or have ideas, get in touch with me at @luke_dot_js on
or @luke in the Elm Slack!

Posted by Trouble (::)entrating on Sat, 02/27/2016 - 19:36

The (|>) operator is, in my opinion, one of the most elegant features of the
Elm language. It lets us read function applications in the order the actually
happen and this readability gain can have a hugely positive influence on our API
and application design. While this operator is generally useful for expressing
data transformations, it has a particularly nice fit for building large or
high-complexity configuration objects more expressively.

Consider as a primary
candidate the low-level request interface in elm-http. Let’s say we want to
send a PATCH request to a cross-origin endpoint and we need to include a
cookie for authentication and we only want to wait 5 seconds for the request to
complete. Doing so requires two configuration objects:

import Http

request : Http.Request
request =
{ verb = "PATCH"
, headers =
[ ("Origin", "")
, ("Access-Control-Request-Method", "PATCH")
, ("Content-Type", "application/json")
, url = ""
, body = Http.string (encodeItemUpdate itemUpdate)

settings : Http.Settings
settings =
{ Http.defaultSettings
| timeout = 5 * Time.second
, withCredentials = True

result : Task Http.RawError Http.Response
result =
Http.send settings request

This is a pretty obnoxious amount of work to send a request with parameters
that are not too far outside the norm. This is, of course, as it should be as
elm-http is intended to be a low-level interface to XMLHttpRequest in Elm
and not anything fancy or expressive. But as humans we want something that is
easy to read and understand. This is the intention of
elm-http-extra, and it will
serve as an excellent example of using (|>) to build configuration as needed
instead of supplying it all at once.
A roughly equivalent request using elm-http-extra looks like the following:

import Http.Extra as HttpExtra exposing (..)

result : Task (HttpExtra.Error ApiError) (HttpExtra.Response Item)
result =
HttpExtra.patch ""
|> withHeader ("Origin", "")
|> withHeader ("Access-Control-Request-Method", "PATCH")
|> withHeader ("Content-Type", "application/json")
|> withStringBody (encodeItemUpdate itemUpdate)
|> withTimeout (5 * Time.second)
|> withCredentials
|> send decodeItem decodeApiError

The advantages of the above are as follows:

  • We don’t need to know anything about the underlying configuration objects
  • We only need to learn or remember the functions which are relevant to the
    the task at hand
  • We can easily remove the final send and express the whole configuration
    building process as a separate function that is easy to test by doing
    equality comparison on the output.
  • We can combine existing operators into more high-level ones that are totally

As an example of the final point, we can extract the headers out into a separate
function that expresses the header needs of every API request and even use the
contents of the request configuration builder record to help us out:

import Http.Extra as HttpExtra exposing (..)

withApiHeaders : HttpExtra.RequestBuilder -> HttpExtra.RequestBuilder
withApiHeaders builder =
verb =
.verb (HttpExtra.toRequest builder)
|> withHeader ("Origin", "")
|> withHeader ("Access-Control-Request-Method", verb)
|> withHeader ("Content-Type", "application/json")

result : Task (HttpExtra.Error ApiError) (HttpExtra.Response Item)
result =
HttpExtra.patch ""
|> withApiHeaders
|> withStringBody (encodeItemUpdate itemUpdate)
|> withTimeout (5 * Time.second)
|> withCredentials
|> send decodeItem decodeApiError

Using the (|>) operator in this way allows us to build totally extensible DSL-
like APIs without all of the complexity of an actual DSL because everything is
just a function with a very specific type of interface. This even allows us to
abstract actual DSLs in a much nicer and easier-to-understand way. Consider
regular expressions, the confusing DSL to end them all. Using this technique
with the
package we can express a Regex using a human-readable, chainable interface.
Instead of constantly re-learning regular expressions to perform a simple task
like match a correctly formatted url, we can write the following:

import Regex exposing (Regex)
import VerbalExpressions as Verex exposing (..)

tester : Regex
tester =
|> startOfLine
|> followedBy "http"
|> possibly "s"
|> followedBy "://"
|> possibly "www."
|> anythingBut " "
|> endOfLine
|> toRegex

Every call in the pipeline above is just a function which operates on some
configuration builder which we can ultimately transform into a result which
would have been harder to obtain on its own. In the case of elm-http it was a
Task a b for the request result which took a lot of configuration, and in
this case it is a Regex which would have required a hard-to-understand
regular expression string.

Let’s try and generalize a few things about this pattern. The process of using a
chainable API with (|>) involves three stages, initialize, build, and
compile. During the initialize stage we start with generate a base object of a
particular type, called the builder. It could be a totally new record type or
a union type that wraps existing configuration types. In the case of
elm-http-extra it is a union type which holds an Http.Request and an
Http.Settings together. The value obtained during the initialize step should
always be valid for compilation straight away, which means several
initialization functions might be required.

This is the case with elm-http-extra since every request must have at least a
verb and a url. The case is much simpler with elm-verbal-expressions, since we
can start every VerbalExpression from a single base value. In this case we
just expose the one initial value and take advantage of immutability in Elm to
allow chaining from the basic case.

During the build step we make use of the (|>) operator by enforcing a specific
signature for the functions in our API. Every function must accept a builder as
its final argument and return a builder as its return value. Any additional
arguments should precede the builder. This data-last signature is a common style
in functional programming as it also makes function composition easy.

The compile step allows to actually make use of the builder. Compilation
functions can have any number of purposes, like extraction of data for testing
or actually running a Task. For example, in elm-http-extra there are three
compilation functions - toRequest, toSettings, and send. The first two
allow us to extract and inspect information from the builder whereas send
actually creates a Task. Compilation functions that do not create a Task can
be used during the build step as well since they are pure.


As the amount of configuration or mental overhead to perform a particular
operation increases it can be useful to dissociate the various components of
that configuration into a series of smaller, step-wise operations. Such an
effort is best expressed through functions that play to the strengths of the
(|>) operator. As examples, elm-http requires much configuration for many
common request cases, and regular expressions require a lot of research and
preparation to use correctly. Through the use of chainable APIs and then (|>),
elm-http-extra and elm-verbal-expressions make those processes easy to
read and maintainable. The success of this API design in these cases can be
easily applied to many similar use cases by simply following the initialize-
build-compile pattern.

Posted by on Mon, 02/15/2016 - 02:00

On Wednesday last week I flew over to London from Amsterdam to attend the newly formed Elm London’s inaugural meetup. I was glad to have RSVPed early as the event quickly became oversubscribed with the waiting list outnumbering the attendee list three to one. That in itself is a clear indication of the kind of interest in Elm from all quarters.

Continue reading…

Posted by dennisreimann on Thu, 02/04/2016 - 13:00

By defining a union type one always creates a new type that did not exist before. A union type can be an amalgamation of different types – but it does not have to be.

Posted by dennisreimann on Mon, 02/01/2016 - 13:00

Records and tuples can contain an arbitrary amount of elements – as opposed to lists, arrays, sets, and dictionaries these elements do not have to be of the same data type.

Posted by dennisreimann on Wed, 01/27/2016 - 13:00

In Elm there are different kinds of data structures that can contain elements. This article spotlights the iterable structures lists, arrays, sets, and dictionaries.

Posted by dennisreimann on Thu, 01/21/2016 - 13:00

This article spotlights the central construct of the Elm programming language: Functions. What does the definition of a function look like, how can functions be chained via piping and what the heck is currying?

Posted by FullyForged on Thu, 01/21/2016 - 12:30

When working with Phoenix channels and Elm it may be useful to keep track of the websockets connection status. In this blog post, we’ll see how this can be accomplished by leveraging interoperability.

Posted by dennisreimann on Tue, 01/19/2016 - 02:00

Importing a module exposes its functionality in the context of the program that is loading the module. In Elm there are different ways to import modules and we will have a look at these in this article.

Posted by Rundis on Tue, 01/19/2016 - 01:00

Any serious Single Page Application needs to have routing. Right ? So before we add
any further pages it’s time to add routing support to the Elm frontend.

In episode 2, we implemented
a Micky Mouse solution for page routing. Clearly that approach won’t scale. Now is a good time to
implement something that can handle multiple pages, history navigation, direct linking etc.
We could do it all from scratch, but lets opt for pulling in a library.
In this episode we’ll introduce elm-transit-router
to the Albums sample application.

Useful resources

  • Check out the other episodes in this blog series.
  • The accompanying Albums sample app is on github, and there is a tag
    for each episode


I decided pretty early on to try out the elm-transit-router library.
It seemed to cover most of what I was looking for. It even has some pretty cool support for animations when doing page transitions.

Static typing is supposed to be really helpful when doing refactoring. Introducing routing should be a nice little excersize to
see if that holds. Remember, there still isn’t a single test in our sample app, so it better hold.
The elm-transit-router library github repo contains a great example app that proved very helpful
in getting it up and running for the Albums app.

Hop is an alternative routing library you might want to check out too.

Implementation changes


// (...
"source-directories": [
"src/" (1)

// ...
"dependencies": {
//... others ommitted
"etaque/elm-route-parser": "2.1.0 <= v < 3.0.0", (2)
"etaque/elm-transit-style": "1.0.1 <= v < 2.0.0", (3)
"etaque/elm-transit-router": "1.0.1 <= v < 2.0.0" (4)


We’ve moved all elm files but Main.elm to the a src sub directory. So we need to add src to the list of source directories

A typed route parser with a nice DSL in Elm: We use it for defining our routes

Html animations for elm-transit

Drop-in router with animated route transitions for single page apps in Elm. Drop in, as in fitting very nicely with elm start-app.

Album dependencies
Click for larger diagram

The addition of the 3 new dependencies also adds quite a few transitive dependencies. The diagram
above is automatically generated by the elm-light plugin for Light Table.

Defining routes (frontend/src/Routes.elm)

type Route (1)
= Home
| ArtistListingPage
| ArtistDetailPage Int
| NewArtistPage
| EmptyRoute

routeParsers : List (Matcher Route)
routeParsers =
[ static Home "/" (2)
, static ArtistListingPage "/artists"
, static NewArtistPage "/artists/new"
, dyn1 ArtistDetailPage "/artists/" int "" (3)

decode : String -> Route
decode path = (4)
RouteParser.match routeParsers path
|> Maybe.withDefault EmptyRoute

encode : Route -> String
encode route = (5)
case route of
Home -> "/"
ArtistListingPage -> "/artists"
NewArtistPage -> "/artists/new"
ArtistDetailPage i -> "/artists/" ++ toString i
EmptyRoute -> ""

Union type that defines the different routes for the application

A static route matcher (static is a function from the RouteParser dsl)

Dynamic route matcher with one dynamic param

We try to match a given path with the route matchers defined above. Returns route of first successful match, or the EmptyRoute route
if no match is found.

Encode a given route as a path

A few handy router utils (frontend/src/Routes.elm)

redirect : Route -> Effects ()
redirect route = (1)
encode route
|> Signal.send TransitRouter.pushPathAddress
|> Effects.task

clickAttr : Route -> Attribute
clickAttr route = (2)
on "click" Json.value (\_ -> Signal.message TransitRouter.pushPathAddress <| encode route)

linkAttrs : Route -> List Attribute
linkAttrs route = (3)
path = encode route
[ href path
, onWithOptions
{ stopPropagation = True, preventDefault = True }
(\_ -> Signal.message TransitRouter.pushPathAddress path)

This function allows us to perform routing through a redirect kind of effect. Comes in handy when we need to switch
routes as a result of performing a task or doing an update action of some sort.

Helper function that creates a click handler attribute. When clicked the signal is forwarded to an address of the internal mailbox for the
elm-transit-router library. By means of delegation the internal TransitRouter.Action type is wrapped into our app’s Action type.
We’ll get back to this when we wire it all together !

Another helper function, similar to clickAttr, but this is more specific for links that also has a href attribute

Changes in Main.elm

Too hook in elm-transit-router we need to make a couple of changes to how we wire up our model, actions, view and update function.
It’s also worth noting that from episode 2 have removed all direct update delegation from ArtistListing to ArtistDetail, this now
all will happen through route transitions. An immediate benefit of that is that the ArtistDetail page becomes much reusable.

Model, actions, transitions and initialization

type alias Model = WithRoute Routes.Route (1)
{ homeModel : Home.Model
, artistListingModel : ArtistListing.Model
, artistDetailModel : ArtistDetail.Model

type Action =
| HomeAction Home.Action
| ArtistListingAction ArtistListing.Action
| ArtistDetailAction ArtistDetail.Action
| RouterAction (TransitRouter.Action Routes.Route) (2)

initialModel : Model
initialModel =
{ transitRouter = TransitRouter.empty Routes.EmptyRoute (3)
, homeModel = Home.init
, artistListingModel = ArtistListing.init
, artistDetailModel = ArtistDetail.init

actions : Signal Action
actions = RouterAction TransitRouter.actions (4)

mountRoute : Route -> Route -> Model -> (Model, Effects Action)
mountRoute prevRoute route model = (5)
case route of

Home ->
(model, Effects.none)

ArtistListingPage -> (6)
(model, ArtistListingAction (ServerApi.getArtists ArtistListing.HandleArtistsRetrieved))

ArtistDetailPage artistId ->
(model, ArtistDetailAction (ServerApi.getArtist artistId ArtistDetail.ShowArtist))

NewArtistPage ->
({ model | artistDetailModel = ArtistDetail.init } , Effects.none)

EmptyRoute ->
(model, Effects.none)

routerConfig : TransitRouter.Config Routes.Route Action Model
routerConfig = (7)
{ mountRoute = mountRoute
, getDurations = \_ _ _ -> (50, 200)
, actionWrapper = RouterAction
, routeDecoder = Routes.decode

init : String -> (Model, Effects Action)
init path = (8)
TransitRouter.init routerConfig path initialModel

We extend our model using WithRoute for our Route type in routes. This extends our type with a transitRouter property

We add a RouteAction to our Action type. We will handle that explicitly in the update function we’ll cover in the next section

We define an initial model, which has the initial models for the various pages. In addition we initialize the transitRouter property
with an empty state and EmptyRoute route (that didn’t read to well). Basically a route that shouldn’t render anything, because it will transition
to an actual route. It’s just an intermediary

Transformer for mapping TransitRouter actions to our own RouterAction. This allows start-app to map external input signals to inputs with an action type our application
can recognize and process.

mountRoute is a function that provides what we want to happen in our update when a new route is mounted. Currently we
only pattern match on route to be mounted, but we could also match on the combination of previous route and new route to provide
custom behaviour depending on where you came from and where your are going to. Very powerful !

When the ArtistListingPage route is mounted we return an effect to retrieve artists (when that effect returns the ArtistListing.HandleArtistRetrieved action is then eventually passed to the update function of ArtistListing)

routerConfig wires together the various bits that TransitRouter needs to do it’s thing

The init function now just initializes the TransitRouter with our config, and initial path (which we receive from a port) and our Initial model

There’s quite a bit going on here, but once this is all in place, adding new routes is quite a breeze. I’d recommend reading
through the Readme for elm-transit-router to understand more about the details of each step

The update function

update : Action -> Model -> (Model, Effects Action)
update action model =
case action of

NoOp ->
(model, Effects.none)

HomeAction homeAction ->
let (model', effects) = Home.update homeAction model.homeModel
in ( { model | homeModel = model' }
, HomeAction effects )

ArtistListingAction act -> (1)
let (model', effects) = ArtistListing.update act model.artistListingModel
in ( { model | artistListingModel = model' }
, ArtistListingAction effects )

ArtistDetailAction act -> (2)
let (model', effects) = ArtistDetail.update act model.artistDetailModel
in ( { model | artistDetailModel = model' }
, ArtistDetailAction effects )

RouterAction routeAction -> (3)
TransitRouter.update routerConfig routeAction model

You should recognize this pattern from the previous episode. We delegate all actions tagged with ArtistListingAction
to the update function for ArtistListing. The we update the model with the updated model from ArtistListing and
map any effects returned.

If you remember from episode 2 this used to reside in ArtistListing, but
has been moved here.

RouterAction action types are handled by the update function in TransitRouter. If you Debug.log this function you will see this
is called repeadly when there is a transition from one route to the next. (To handle the animation effects most notably)

The main view/layout

menu : Signal.Address Action -> Model -> Html
menu address model = (1)
header [class "navbar navbar-default"] [
div [class "container"] [
div [class "navbar-header"] [
div [ class "navbar-brand" ] [
a (linkAttrs Home) [ text "Albums galore" ]
, ul [class "nav navbar-nav"] [
li [] [a (linkAttrs ArtistListingPage) [ text "Artists" ]] (2)

contentView : Signal.Address Action -> Model -> Html
contentView address model = (3)
case (TransitRouter.getRoute model) of
Home ->
Home.view (Signal.forwardTo address HomeAction) model.homeModel

ArtistListingPage -> (4)
ArtistListing.view (Signal.forwardTo address ArtistListingAction) model.artistListingModel

ArtistDetailPage i ->
ArtistDetail.view (Signal.forwardTo address ArtistDetailAction) model.artistDetailModel

NewArtistPage ->
ArtistDetail.view (Signal.forwardTo address ArtistDetailAction) model.artistDetailModel

EmptyRoute ->
text "Empty WHAT ?"

view : Signal.Address Action -> Model -> Html
view address model =
div [class "container-fluid"] [
menu address model
, div [ class "content"
, style (TransitStyle.fadeSlideLeft 100 (getTransition model))] (5)
[contentView address model]

Menu view function for the app

Here we use the linkAttrs util function from Routes.elm to get a click handler. When the link is click
a route transition to the given page will occur (with addressbar update, history tracking and the whole shebang)

We render the appropriate main content view based which route is current in our model.

Getting the view for a page is used in the typical start-app way. Call the view function of the sub component and make sure
to provide a forwarding addres that main can handle in its update function !

We define the route transition animation using the style attribute (function) in elm-html. Here we use a transition style
defined in elm-transit-style.

How to navigate from one page to another ?

Move from artistlisting to artistdetail (frontend/src/ArtistListing.elm)

artistRow : Signal.Address Action -> Artist -> Html
artistRow address artist =
tr [] [
td [] [text]
,td [] [button [ Routes.clickAttr <| Routes.ArtistDetailPage ] [text "Edit"]] (1)
,td [] [button [ onClick address (DeleteArtist (.id artist))] [ text "Delete!" ]]

view : Signal.Address Action -> Model -> Html
view address model =
div [] [
h1 [] [text "Artists" ]
, button [
class "pull-right btn btn-default"
, Routes.clickAttr Routes.NewArtistPage (2)
[text "New Artist"]
, table [class "table table-striped"] [
thead [] [
tr [] [
th [] [text "Name"]
,th [] []
,th [] []
, tbody [] ( (artistRow address) model.artists)

For navigation using links we just use the util function Routes.clickAttr function we defined earlier. This will trigger the necessary
route transition to the appropriate page (with params as necessary)

It’s worth noting that we since episode 2 have introduced a separate route for handling NewArtist (/artists/new). We are still
using the same behaviour otherwise, so it’s just a minor modification to have a separate transition for a new artist (since that doesn’t have a numeric id as part of its route path)

Move to the artist listing after saving an artist (frontend/src/ArtistDetail.elm)

-- ... inside update function

HandleSaved maybeArtist ->
case maybeArtist of
Just artist ->
({ model | id = Just
, name = }
, (\_ -> NoOp) (Routes.redirect Routes.ArtistListingPage) (1)

Nothing ->
Debug.crash "Save failed... we're not handling it..."

We use the Routes.redirect function we defined earlier. When the task fro saving is completed we trigger an effect
that will transtion route to the ArtistListing page. To allow the effect to work in our update function we need to map it to
an action that ArtistDetail knows about (we don’t have access to the RouterAction in main here!). That’s why we map the effect
to a NoOp action.

The final wiring


app : StartApp.App Model
app =
{ init = init initialPath (1)
, update = update
, view = view
, inputs = [actions] (2)

main : Signal Html
main =

port tasks : Signal (Task.Task Never ())
port tasks =

port initialPath : String (3)

We call the init function previously defined with a initialPath (which we get from a port, see 3 below)

The inputs fields of the start-app config is for external signals. We wire it to our actions defintion defined earlier

We get the initialPath through a port from JavaScript. See the next section for how

INFO: Initially I forgot to wire up the inputs. The net result of that was that none of the links actually did anything.
Was lost for a while there, but the author of elm-transit-router etaque was able to spot it easily
when I reached out in the elm-lang slack channel


<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
<script type="text/javascript" src="main.js"></script> (1)
<script type="text/javascript" src="/_reactor/debug.js"></script> (2)

<script type="text/javascript">
var main = Elm.fullscreen(Elm.Main, {initialPath: "/"}); (3)


This is the transpiled elm to js for our frontend app

We don’t really need this one, but if reactor in debug mode had worked with ports this would be necessary for debug tracing etc

We start our elm app with an input param for our initialPath. This is sent to the port defined above. It’s currently hardcoded to / (home), but
once we move to a proper web server we would probably use something like window.location.pathname to allow linking directly to
a specific route within our Single Page App.

Summary and next steps

This was an all Elm episode. Hopefully I didn’t loose all Haskellites along the way because of that. We’ve added a crucial
feature for any Single Page (Web) Application in this episode. The end result was pretty neat and tidy too.

So how was the refactoring experience this time ? Well the compiler was certainly my best buddy along the way. Obviously I also
had to consult the documentation of elm-transit-router quite often. i had a few times where things appeared to be compiling fine
in Light Table, but actually there was some error in a Module referred by Main. I’m not sure if it’s make’s fault or just that there is
something missing in the elm-light plugin. I’ll certainly look into that. Always handy to have the command line available when you’re
not sure about whether your IDE/Editor is tripping you up or not. I don’t think tests would have caught many of the issues I encountered.
Forgetting to wire up inputs to startapp was probably my biggest blunder, and I’m sure no test would have covered that. I needed to know that this
was something I had to wire up for it to work. RTFM etc.

Next up I think we will look at how much effort there is to add additional features. The hypothesis is that it should be
fairly straighforward, but who knows !