Elm Lang

Functional

Typed

Reactive

Immutable

Compile to JS

-- comments are two dashes

-- strings
"hello"

"hello" ++ " world" -- hello world

-- basic maths

2 + 3 * 4

9 / 2 -- 4.5
9 // 2 -- 4

-- conditionals

if True then "hello" else "world"
-- returns "hello"

-- there's no `return`, everything is an expression

if 2 then "hello" else "world"
-- invalid, no notion of "truthiness"
-- lists

["Jack", "Will", "James"]

["Jack", 54, 5.4123]
-- invalid, all items must be same type


List.isEmpty [] -- True

List.length [1, 2, 3] -- 3

-- anonymous functions
-- note use of let/in to create locally scoped named expressions
-- (aka variables, sort of)
let
  names = ["Jack", "Will", "James"]
in
  List.map (\name -> String.toUpper name) names
  -- ["JACK", "WILL", "JAMES"]
-- functions

isNegative n =
  n < 0

isNegative 3 -- False

isNegative -5 -- True

-- type annotations
-- the compiler can infer these
-- but it's good practice to do it!

isNegative : Int -> Bool
isNegative n = n < 0

add : Int -> Int -> Int
add x y = x + y

add 2 2 -- 4

addTwo : Int -> Int
addTwo = (add 2)
addTwo 2 -- 4

-- better, partial application of built in
addTwo = ((+) 2)
names = ["Jack", "Will", "James"]

-- not bad
List.map (\name -> String.toUpper name) names

-- better!
List.map String.toUpper names

-- sum some numbers
numbers = [1, 2, 3]

List.foldl (\x y -> x + y) 0 numbers

-- but wait!

List.foldl (+) 0 numbers

-- (there is a sum built in though)

List.sum numbers
-- tuples
-- fixed number of values, of any type

validateName : String -> (Bool, String)
validateName string =
  if String.length < 20 then
    (True, "Name is OK!")
  else
    (False, "name is too long")

-- records

person = { name = "Jack", age = 23 }

-- updating fields in records
olderPerson = { person | age = 60 }

-- accessing values of keys
person.name -- "Jack"
.name person -- "Jack"

people = [{ name = "Jack" }, { name = "Will" }]
-- OK...
List.map (\p -> p.name) people -- ["Jack", "Will"]

-- OMG Elm <3
List.map .name people -- ["Jack", "Will"]

-- enumerations (or: Custom Types)

type TodoState = NotStarted | InProgress | Done

todoText : TodoState -> String
todoText state =
  case state of
    NotStarted -> "Not yet begun!"
    InProgress -> "Working on it"
    Done -> "Finished!"

-- you can also associate extra info
type User = Anonymous | LoggedIn String

userPhoto : User -> String
userPhoto user =
  case user of
    Anonymous -> "anon.png"
    -- pattern matching
    LoggedIn name -> "users/" ++ name ++ ".png"
-- no more nulls

-- built into Elm, but here for clarity!
type Maybe a = Just a | Nothing

type UserName = Maybe String

displayUserName : UserName -> String
displayUserName name =
  case UserName of
    Nothing -> "unknown"
    Just name -> "Name is " ++ name


returnThingIfGreaterThanTwo : Int -> Maybe Int
returnThingIfGreaterThanTwo x =
  if x > 2 then
    Maybe 5
  else
    Nothing
-- type aliases

type alias Person =
  { name : String, age : Int }

printPerson : Person -> String
printPerson person =
  person.name ++ " is " ++ (toString person.age) ++ " years old"

-- it's not just aliases that you can alias

type alias Coords = (Int, Int)

printCoords : Coords -> String
printCoords coords =
  (toString (fst coords)) ++ ", " ++ (toString (snd coords))

-- pattern matching arguments
printCoords (x, y) =
  (toString x) ++ ", " ++ (toString y)

let
  names = ["Jack", "Will", "James"]
  upperCase = List.map String.toUpper names

  -- split each string into List of strings of 1 str each
  -- List.concatMap is effectively a flatMap

  split = List.concatMap (String.split "") upperCase

  -- convert each string into a char

  chars = List.concatMap String.toList split

  -- change each char into its keycode
  numbs = List.map Char.toCode chars
in
  toString numbs
-- [74,65,67,75,87,73,76,76,74,65,77,69,83]
let
  numbs = ["Jack", "Will", "James"]
    |> List.map String.toUpper
    |> List.concatMap (String.split "")
    |> List.concatMap String.toList
    |> List.map Char.toCode
in
  toString numbs

-- more generally:

x |> f is the equivalent of (f x)

a |> b |> c is the equivalent of (c (b a))

-- there is also <| for when that version reads better

Reactivity with Signals

 

Signal: a value that changes over time

 

"Streams" in other languages (roughly).

Mailboxes

 

  • an address you can send messages to
  • a Signal of all the messages it's been sent

Model - View - Update

 

Model: state / data in the application

 

Update: updates the state based on an action

 

View : renders the state to HTML

-- MODEL

type alias Model = { ... }

-- MAILBOX FOR USER ACTIONS

actions : Signal.Mailbox Action
actions = 
  Signal.mailbox NoOp

-- UPDATE

type Action = NoOp | ...

update : Action -> Model -> Model
update action model =
  case action of
    NoOp -> ...
    ..

-- VIEW

view : Model -> Html
view model = 
  ...

Live demo time!

https://github.com/jackfranklin/elm-counter-example/

Interopability with JavaScript

https://github.com/evancz/elm-architecture-tutorial/

http://elm-lang.org/guide/reactivity

http://elm-lang.org/guide/interop

https://github.com/jackfranklin/elm-game-of-life

 

https://github.com/jackfranklin/elm-connect-four

 

https://github.com/jackfranklin/elm-carcassonne

 

https://github.com/jackfranklin/elm-statey

elm-lang-intro

By Jack Franklin