The main focus for Elm at present is to make HTML applications. With that in mind, we'll make our first HTML application. Let's get started.
We'll start off by making a new directory and using elm-package to install elm-html:
mkdir counter
cd counter
elm-package install elm-lang/html
Now we're just going to start off with the most basic starting Elm project - a counter that you can increment or decrement. Let's make our Main.elm file:
vim Main.elm
We'll start off just producing some HTML. To do this, we'll import the Html module to start:
module Main exposing (..)
import Html exposing (..)
Here, we're starting a new module, called Main
, and exposing all of it's
functions. Then, we're importing everything from the Html module, which is
provided by the elm-lang/html
package we installed. Let's make a main
function that returns some Html:
main =
div [] [ text "Hello World" ]
This is using the div
function from html
. All of these functions take two
list arguments. The first is a list of attributes, and the second is a list of
child elements. In this case we have a div, with no arguments, with a single
text node as a child.
elm-reactor
We can run this using the elm-reactor for fast development. In the project
directory, just run elm-reactor
.
elm-reactor
Now you can visit http://localhost:8000 and click on Main.elm
to run it.
The reactor is a really fast way to develop in Elm.
Let's make the content an Int
and call toString
on it, since the text
function takes a string argument:
main =
div [] [ text (toString 1) ]
Alright, so this is a very basic use of html
. Let's add a couple of buttons
for incrementing and decrementing the counter:
main =
div []
[ button [] [ text "-" ]
, div [] [ text (toString 1) ]
, button [] [ text "+" ]
]
So here we just added a div containing all two action buttons and the counter text. Next, we'll move on to building out the app's functionality.
We'll start off with our Model
, which defines the state for our application:
type alias Model =
Int
Since we just have a counter, we need to track a single Int
as our state.
This is using a type alias, so we're saying the type for Model
is the same as
the type for Int
.
Msg
typeYou can do two things with this model - Increment
it and Decrement
it.
These are our two messages. We'll define a union type for them:
type Msg
= Increment
| Decrement
Union Type
sounds like something fancy, but it's literally just a few types
joined together into a single type.1 In this case, we're saying that a Msg
type is either Increment
or Decrement
.
Next, we'll define an update
function. This is the first time we're seeing
functions, so I'll try to be very explicit. First, we'll define the type
signature for the function.
Our update
function takes a Msg
and a Model
and returns a Model
. We can
describe that with the following type signature:
update : Msg -> Model -> Model
Now we'll define the function. Here we'll define a function that takes 2 arguments, and we'll define a case statement on the message argument:
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
Here we're saying that if our message is Increment
, we add 1 to the model. If
it's Decrement
, we subtract 1 from the model. The last expression in a function
is the returned value, so here we've satisfied our type signature in that we are
just returning an Int
, which is all our Model
type is.
If you're used to seeing parenthesis and curly braces everywhere when defining functions, this might look a bit weird. The syntax for Elm is extremely sparse, and after a little usage it starts to feel rather natural. I do remember this syntax throwing me for a loop initially though.
OK, so this defines all of the functional operation of our application. We also want to be able to see the model though, so let's do that.
We'll define a function called view
. Elm has a very standard way to build
applications. Our view function takes a Model
and returns a type of HTML
that produces Messages
(Html Msg
).
view : Model -> Html Msg
So here's our view
type signature. For now, you can think of it as just
taking in a Model
and producing some Html
, which is a type defined by the
elm-lang/html
package. There's also the Msg
bit, which I'll explain
momentarily.
Now we'll copy in the existing main
function definition here, replacing the
hard-coded 1 with the model
argument. We'll define a new main
in a moment.
view : Model -> Html Msg
view model =
div []
[ button [] [ text "-" ]
, div [] [ text (toString model) ]
, button [] [ text "+" ]
]
Now for our main
function, we're going to use something that Elm provides
called beginnerProgram
. It is an easy way of wiring a model, update, and view
together to make an application. It comes from the html
package.
main =
Html.beginnerProgram
{ model = 0
, view = view
, update = update
}
We'll look at it in the reactor. It looks like you'd expect - we have our buttons and our model as a string.
The next thing we want to do is start to wire in messages from the buttons.
This is where the Msg
comes in. In our view function, we want to produce the
Increment
and Decrement
messages when the appropriate buttons are pressed.
We do this in the attributes for the buttons:
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]
If we try to run it now though, we'll get an error because onClick
is not
defined. This comes from html's Html.Events
module, so let's import this
function from it:
import Html.Events exposing (onClick)
Now if you refresh the page, we have a functioning counter with buttons that modify it.
So that's our first HTML app with Elm. It's pretty fantastically simple, to be honest. The nicest part is this: you now know what Elm applications look like. Every app you see will be a variation on this pattern. See you soon!
You'll also hear these referred to as Algebraic Data Types
, or ADTs
↩