Sunday, August 16, 2015

An Opening Example of Elm: building HTML by parsing parameters

I never enjoyed front-end development, until I found Elm. JavaScript with its `undefined`, its untyped functions, its widely scoped mutable variables. It's like Play-Doh, it's so malleable. And when I try to make a sculpture, the arms fall off. It takes a lot of skill to make Play-Doh look good.

Then Richard talked me into trying Elm. Elm is more like Lego Technics. Fifteen years ago, I bought and built a Lego Technics space shuttle, and twelve years ago I gave up on getting that thing apart. It's still in my attic. Getting those pieces to fit together takes some work, but once you get there, they're solid. You'll never get "method not found on `undefined`" from your Elm code.


Elm is a front-end, typed functional language; it to JavaScript for use in the browser. It's a young language (as of 2015), full of opportunity and surprises. My biggest surprise so far: I do like front-end programming!

To guarantee that you never get `undefined` and never call a method that doesn't exist, all Elm functions are Data in, Data out. All data is immutable. All calls to the outside world are isolated. Want to hit the server? Want to call a JavaScript library? That happens through a port. Ports are declared in the program's main module, so they can never hide deep in the bowels of components. Logic is in one place (Elm), interactions in another.
one section (Elm) has business logic and is data-in, data-out. It has little ports to another section( JavaScript) that can read input, write files, draw UI. That section blurs into the whole world, including the user.


This post describes a static Elm program with one tiny port to the outside world. It illustrates the structure of a static page in Elm. Code is here, and you can see the page in action here. The program parses the parameters in the URL's query string and displays them in an HTML table.[1]

All web pages start with the HTML source:
<html><head>
  <title>URL Parameters in Elm</title>
  <script src="elm.js" type="text/javascript"></script>
  <link href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css" rel="stylesheet"></link>
</head>
<body></body>
<script type="text/javascript">
  var app = Elm.fullscreen(Elm.UrlParams,
                           { windowLocationSearch:
                               window.location.search
                           });
</script></html>

This brings in my compiled Elm program and some CSS. Then it calls Elm's function to start the app, giving it the name of my module which contains main, and extra parameters, using JavaScript's access to the URL search string.

Elm looks for the main function in my module. The output of this function can be a few different types, and this program uses the simplest one: Html. This type is Elm's representation of HTML output, its virtual DOM.

module UrlParams where

import ParameterTable exposing (view, init)
import Html exposing (Html)

main : Html
main = view (init windowLocationSearch)

port windowLocationSearch : String
The extra parameters passed from JavaScript arrive in the windowLocationSearch port. This is the simplest kind of port: input received once at startup. Its type is simply String. This program uses one custom Elm component, ParameterTable. The main function uses the component's view function to render, and passes it a model constructed by the component's init method.

Somewhere inside the JavaScript call to Elm.fullscreen, Elm calls the main function in UrlParams, converts the Html output into real DOM elements, and renders that in the browser. Since this is a static application, this happens once. More interesting Elm apps have different return types from main, but that's another post.

From here, the data flow of this Elm program looks like this:
The three layers are: a main module, a component, and a library of functions.
The main module has one input port for the params.  That String is transformed by init into a Model, which is transformed by View into Html. The Html is returned by main and rendered in the browser. This is the smallest useful form of the Elm Architecture that I came up with.

Here's a piece of the ParameterTable module:
module ParameterTable(view, init) where

import Html exposing (Html)
import UrlParameterParser exposing (ParseResult(..), parseSearchString)

--- MODEL
type alias Model = { tableData: ParseResult }

init: String -> Model
init windowLocationSearch =
  { tableData = parseSearchString windowLocationSearch }

--- VIEW
viewModel -> Html
view model =
  Html.div ...
The rest of the code has supporting functions and details of the view. These pieces (Model, init, and view) occur over and over in Elm. Often the Model of one component is composed from the Models of subcomponents, and the same with init and view functions.[2]

All the Elm files are transformed by elm-make into elm.js. Then index.html imports elm.js and calls its Elm.fullscreen function, passing UrlParams as the main module and window.location.search in the extra parameter. And so, a static (but not always the same) web page is created from data-in, data-out Elm functions. And I am a happy programmer.



[1] Apparently there's not a built-in thing in JavaScript for parsing these. Which is shocking. I refused to write such a thing in JavaScript (where by "write" I mean "copy from StackOverflow"), so I wrote it in Elm.

[2] Ditto with update and Action, but that's out of scope. This post is about a static page.





No comments:

Post a Comment