Elm: Beyond the Basics

This marks the end of our crash course programming in Elm. We have all we need to use Elm as our implementation language for the rest of our course topics. For your projects, you may want to poke around and learn more; some pointers below.

Elm Architecture

There is a start-app library which hides most of "plumbing" involved with using signals for basic purposes. So far, we have been programming explicitly with signals to emphasize the functional reactive paradigm.

As you develop larger applications (i.e. your course project), you will want to consider using these more full-featured libraries. At that point, consult the Elm Architecture for guidance.

"Raw" HTML

The core libraries Graphics.Element and Graphics.Collage provide an API for a small (and somewhat low-level) subset of the full HTML (+ CSS + SVG) specifications. Additional libraries — including elm-html, elm-svg, and markdown — provide more complete, "thin wrapper" access to the entire target language. Although the former libraries are no longer the preferred way to write HTML applications in modern Elm (times change quickly with a young language!), we started by using them nonetheless.

Demo: Local Storage via Ports

HTML5 Local Storage

See LocalStorage.html for an example of how to use JavaScript to save information across visits to a webpage (on a per-domain basis).

Elm Ports

Outgoing ports are used to communicate from Elm to JavaScript, and incoming ports are used to communicate from JavaScript to Elm.

Define an Elm module CountPageVisits.elm with ports, which act like "external" Signals:

module CountPageVisits where

import Graphics.Element exposing (show)
import Mouse

port count : Signal Int
 
port reset : Signal ()
port reset = Mouse.clicks

main =
  Signal.map (\i -> show ("Counter: " ++ toString i)) count

In a JavaScript file Ports.js, load the Elm module Elm.CountPageVisits, and send to its incoming ports and subscribe to its outgoing ports:

function getCount()       { ... }
function incCount()       { ... }
function resetCount()     { ... }
function getAndIncCount() { ... }

var myApp = Elm.fullscreen(Elm.CountPageVisits, {
  count: getAndIncCount()
});

myApp.ports.reset.subscribe(function(event) {
  resetCount();
  myApp.ports.count.send(getAndIncCount());
});

Compile the Elm module to JavaScript...

% elm-make CountPageVisits.elm --output=CountPageVisits.js

... and then put the pieces together in CountPageVisits.html.

Importing Community Packages

So far, we have been using only the core package libraries. There is also a growing number of community packages. Using them is easy. Just define an elm-package.json file that specifies which libraries you want to import.

Say we want to use this HTML package. Add the location of this package to an otherwise boilerplate elm-package.json file (take a look at one generated from elm-make):

{
    "version": "1.0.0",
    "summary": "helpful summary of your project, less than 80 characters",
    "repository": "https://github.com/user/project.git",
    "license": "BSD3",
    "source-directories": [
        "."
    ],
    "exposed-modules": [],
    "dependencies": {
        "elm-lang/core": "3.0.0 <= v < 4.0.0"
        "evancz/elm-html": "4.0.2 <= v < 5.0.0"
    },
    "elm-version": "0.16.0 <= v < 0.17.0"
}

The names of the Elm modules exported by this package are listed at the top of its documentation page. You can also click the "View Source" on the documentation page and view the elm-package.json file.

Now, import and use the library in an Elm file HtmlTest.elm ...

module HtmlTest where

import Html (..)

main =
     toElement 500 500
  <| div []
  <| [ h1 [] [text "Header 1"]
     , ol [] [li [] [text "one"], li [] [text "two"], li [] [text "three"]]
     , ul [] [li [] [text "one"], li [] [text "two"], li [] [text "three"]]
     , button [] [text "click me"]
     , input [] [text "type here"]
     ]

... and appropriate versions of the libraries will be downloaded locally into an elm-stuff/ directory:

% ls
HtmlTest.elm    elm-package.json

% elm-make HtmlTest.elm --output=HtmlTest.html
Some new packages are needed. Here is the upgrade plan.

  Install:
    elm-lang/core 1.1.0
    evancz/elm-html 2.0.0
    evancz/virtual-dom 1.2.2

Do you approve of this plan? (y/n) y
Downloading elm-lang/core
Downloading evancz/elm-html
Downloading evancz/virtual-dom
Packages configured successfully!
Compiled 38 files
Successfully generated HtmlTest.html

% ls
HtmlTest.elm    HtmlTest.html   elm-package.json  elm-stuff

In subsequent homework and project assignments, you may submit an elm-package.json to declare any community libraries you wish to use.

And if you end up building useful libraries of your own, considering publishing them!

Tasks

Read this if and when you need to do more with the outside world.