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
elm-lang-intro
- 1,114