Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Fold
State
Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Fold
State
Model
Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Sanchez
Html Msg
Fold
State
Model
Wheatley
Our user carefully considers...
Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Sanchez
Fold
Msg
State
Model
Wheatley
Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Sanchez
Html Msg
Fold
State
Model
Wheatley
Elm Runtime
Real World
type Msg
= SummerWheatley
| PedroSanchez
type alias Model =
{ summerWheatley : Int
, pedroSanchez : Int }
init : Model
init =
{ summerWheatley = 0
, pedroSanchez = 0 }
update : Msg -> Model -> Model
update msg model =
case msg of
SummerWheatley ->
{ model |
summerWheatley = model.summerWheatley + 1 }
PedroSanchez ->
{ model |
pedroSanchez = model.pedroSanchez + 1 }
view : Model -> Html Msg
view model = div []
[ text "Vote for:"
, button [ onClick SummerWheatley ] [ text "Wheatley" ]
, button [ onClick PedroSanchez ] [ text "Sanchez" ]
]
Renderer
Sanchez
Html Msg
Fold
Msg
State
Model
Wheatley
The Elm Architecture
is based on the fold-ability of
immutable streams called Signals.
Once the pattern was established, working with
Signals became mostly boilerplate.
Elm has since baked the
programming model into the runtime,
making Signals completely opaque!
(types and good design)
Thank you everyone.
by Daniel Bachler (@danyx23)
this presentation is available at:
slides.com/danielbachler/fearless-refactoring-with-elm
the language
the architecture
the infrastructure
my experience so far
function addNumbers(a, b) {
return a + b;
}
// Whatever
addNumbers(4, "three");
addNumbers a b =
a + b
-- Types are infered to be numbers
-- This is a compile time error
addNumbers 4 "three"
-- Union Type
type Gender
= Male
| Female
| Other String
personAGender = Male
personBGender = Other "Androgynous"
printGender gender =
case gender of
Male ->
"male"
Female ->
"female"
Other selfdefined ->
selfdefined
-- There is no null, no undefined
type alias Person =
{ firstName : String
, lastName : String
, yearOfBirth : Int
}
-- not possible
personA = { firstName = "Lao"
, lastName = "Tse"
, yearOfBirth = null
}
birthYearToString person =
toString person.yearOfBirth
-- instead:
type alias Person =
{ firstName : String
, lastName : String
, yearOfBirth : Maybe Int
}
personA = { firstName = "Lao"
, lastName = "Tse"
, yearOfBirth = Nothing
}
birthYearToString person =
case person.yearOfBirth of
Nothing ->
"Unknown"
Just age ->
toString age
x = 1
x = 2 -- compile error
x = x + 1 -- compile error
y = x + 1 -- ok
addNumbers : Int -> Int -> Int
addNumbers a b =
a + b
birthYearToString : Person -> String
birthYearToString person =
...
addNumbers : Int -> Int -> Int
addNumbers a b =
a + b
result1 = addNumbers 1 2
result2 = addNumbers 1 2
result 1 == result 2 -- True
Calling the same function again with the same arguments will always return the same value, never have any side effects
function addNumbersWeird(a, b) {
window.myGlobalState = window.myGlobalState || 0;
return a + b + (window.myGlobalState++);
}
var result1 = addNumbersWeird(1, 2);
var result2 = addNumbersWeird(1, 2);
result1 == result2; // False
Very similar to React without local state + Redux
Diagram by Evan Czaplicki
See also André Staltz' great overview of unidirectional UI aproaches
-- MODEL
type alias Model = Int
-- UPDATE
type Action = Increment | Decrement
update : Action -> Model -> Model
update action model =
case action of
Increment ->
model + 1
Decrement ->
model - 1
-- VIEW
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ button [ onClick address Decrement ] [ text "-" ]
, div [ countStyle ] [ text (toString model) ]
, button [ onClick address Increment ] [ text "+" ]
]
countStyle : Attribute
countStyle =
style
[ ("font-size", "20px")
, ("font-family", "monospace")
, ("display", "inline-block")
, ("width", "50px")
, ("text-align", "center")
]
-- Wire everything together
main =
start
{ model = 0
, update = update
, view = view
}
elm make
elm repl
elm package
Should we do a hackathon style
meetup in Berlin?
by Daniel Bachler ( @danyx23 on twitter)
Gabriel Grinberg
- Frontend dev @WixAnswers
- Elm e nthusiast
- Mozzie's proud owner -->
Let's face it..
What started as scripting language that was hacked in just 2 weeks, for the browser, ended up being used to build massive web applications.
Since then, there are endless efforts by the community to fix the language's quirks, leading to tools fatigue.
AKA the reason we have the famous JS WAT talk
console.log([]/(Math.sqrt('Bob' + 2))); // NaN :(
<script src="../widgets/widgets_definition.config.js"></script>
<script src="../widgets/initial_widgets_list.val.js"></script>
<script src="../widgets/title-widget/title_widget.ctrl.js"></script>
<script src="../widgets/tickets-widget/tickets-widget.ctrl.js"></script>
<script src="../widgets/tasks-widget/tasks-widget.ctrl.js"></script>
<script src="../widgets/top-issues-widget/top-issues-widget.ctrl.js"></script>
<script src="../widgets/top-issues-widget/top-issues-widget-settings.ctrl.js"></script>
<script src="../widgets/statistics-widget/statistics-widget.ctrl.js"></script>
<script src="../widgets/statistics-widget/statistics-widget-settings.ctrl.js"></script>
<script src="../widgets/online-users-widget/online-users-widget.ctrl.js"></script>
<script src="../widgets/kb-list-widget/kb-list-widget.ctrl.js"></script>
...
...
function calculateTotalPrice(products) {
var totalPrice = products.reduce(function (currentTotal, product) {
return currentTotal + product.price;
});
products.push('bob');
initiateWorldWar4();
return totalPrice;
}
Elm is a purely functional language, designed to run in the browser, made by Evan Czaplicki.
It's not a Framework. Not a library. It's a whole different language.
If it compiles, it runs. No errors.
Highly addictive
Also, Elm's immutable objects are cached internally, leading to better memory management
It also enforces documentation for published packages
Elm uses signals, what you might know as observables from RxJS for event handling, time, and more
Actually, in 0.17 it has changed a lot! Signals are replaced by subscriptions and many things have been simplified
Algebraic Data Types
type Tree = Empty
| Leaf Int
| Node Tree Tree
--
type TodoListAction = Add String
| Remove Int
| Update Int String
| Reset
head: List a -> String
head list =
case list of
x::[] -> "Singleton"
x::xs -> "Not empty"
[] -
Pattern Matching
type Fruit = Apple | Banana | Orange
firstBanana: List Fruit -> Maybe Fruit
firstBanana fruits =
let
bananas = List.filter (\f -> f == Banana) fruits
in
case bananas of
banana::xs -> Just banana
[] -> Nothing
Null Maybe
Tasks make it easy to describe asynchronous operations that may fail, like HTTP requests or writing to a database. Tons of browser APIs are described as tasks in Elm
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
LoadTitle id ->
(model, getTitle id)
ShowError err ->
({model | error = err}, Effects.none)
ShowTitle title ->
({model | title = title}, Effects.none)
getTitle: String -> Effects Action
getTitle id =
Http.getString ("http://cool-api.com/" ++ id ++ "/title")
|> Task.map ShowTitle
|> (flip Task.onError) (\err -> Task.succeed (ShowError err))
|> Effects.task
No probz.
Ports are a general purpose way to communicate with JavaScript. They let you send messages in and out of Elm so you can use JavaScript whenever you need to.
port addUser : Signal (String, UserRecord)
myapp.ports.addUser.send([
"Tom",
{ age: 32, job: "lumberjack" }
]);
myapp.ports.addUser.send([
"Sue",
{ age: 37, job: "accountant" }
]);
Redux
(Prezi, NoRedInk, CircuitHub, Gizra and a few more)
Thomas Brisbout
Freelance JS developer
@tbrisbout
Elm Paris Meetup / 2016-03-23
import Html
main = Html.text "Hello World"
$ npm install -g elm
...
$ elm-repl
---- elm repl 0.16.0 -----------------------------------------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
> 1 + 1
2 : number
>
-- Boolean
True : Bool
False : Bool
-- Boolean
True : Bool
False : Bool
42 : number -- Int or Float depending on usage
3.14 : Float
-- Boolean
True : Bool
False : Bool
42 : number -- Int or Float depending on usage
3.14 : Float
'a' : Char
"abc" : String
-- multi-line String
"""
This is useful for holding JSON or other
content that has "quotation marks".
"""
-- anonymous function
(\ name -> "hello " ++ name)
-- anonymous function
(\ name -> "hello " ++ name)
-- named function
sayHello name = "hello " ++ name
-- anonymous function
(\ name -> "hello " ++ name)
-- named function
sayHello name = "hello " ++ name
-- function application
msg = sayHello "meetup" -- "hello meetup"
sayHello : String -> String
sayHello name = "hello " ++ name
add x y = x + y
add : number -> number -> number
add x y = x + y
Functions in Elm take one argument
add x y = x + y
addOne = add 1
addOne 2 -- returns 3
-- The following are equivalent
add 3 4
(add 3) 4
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
HELLO ! HELLO ! HELLO !
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
Html.text <| String.repeat 3 <| String.toUpper "Hello ! "
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
Html.text <| String.repeat 3 <| String.toUpper "Hello ! "
"Hello !"
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
Html.text <| String.repeat 3 <| String.toUpper "Hello ! "
"Hello !"
|> String.toUpper
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
Html.text <| String.repeat 3 <| String.toUpper "Hello ! "
"Hello !"
|> String.toUpper
|> String.repeat 3
Html.text (String.repeat 3 (String.toUpper "Hello ! ")
Html.text <| String.repeat 3 <| String.toUpper "Hello ! "
"Hello !"
|> String.toUpper
|> String.repeat 3
|> Html.text
double x = x * 2
addOne x = x + 1
(double >> addOne) 2 -- 5
(double << addOne) 2 -- 6
-- Every element in a list must have the same type.
["the", "quick", "brown", "fox"]
list = [1, 2, 3, 4]
listFromRange = [1..4]
listByAppending = [1, 2] ++ [3, 4]
listByPrepending = 1 :: 2 :: 3 :: 4 :: []
-- Every element in a list must have the same type.
["the", "quick", "brown", "fox"]
list = [1, 2, 3, 4]
listFromRange = [1..4]
listByAppending = [1, 2] ++ [3, 4]
listByPrepending = 1 :: 2 :: 3 :: 4 :: []
-- All classical methods are available
List.map (\ n -> n + 1) list -- [2,3,4,5]
List.filter (\ n -> n > 2) list --[3,4]
("elm", 42)
fst ("elm", 42) -- "elm"
snd ("elm", 42) -- 42
error = (404, "Not found")
person =
{ firstName = "John"
, lastName = "Doe"
}
person.firstName -- "John"
.firstName person -- "John"
.firstName { firstName = "Jane" } -- "Jane"
person =
{ firstName = "John"
, lastName = "Doe"
}
{ person |
firstName = "George" }
-- { firstName = "George"
-- , lastName = "Doe"
-- }
person =
{ firstName = "John"
, lastName = "Doe",
, age = 22
}
canDrinkAlcohol {age} =
age > 18
canDrinkAlcohol person -- True
createPerson : String -> String ->
{ firstName : String, lastName : String }
createPerson first last =
{ firstName = first
, lastName = first
}
type alias Person =
{ firstName : String
, lastName : String
}
createPerson : String -> String -> Person
createPerson first last =
{ firstName = first
, lastName = first
}
type Direction =
North | South | East | West
type Answer = Yes | No | Other String
if key == 40 then
n + 1
else if key == 38 then
n - 1
else
n
-- By the way, there
-- is no "Truthy" of "Falsy"
type Shape
= Circle Float
| Rectangle Float Float
area : Shape -> Float
area shape =
case shape of
Circle radius ->
pi * radius ^ 2
Rectangle height width ->
height * width
-- Qualified imports
import Html
import Html.Attributes
main = Html.div "test"
-- exposing single identifiers
import Html exposing (div, h1, p)
-- exposing everything
import Html.Attributes exposing (..)
-- import alias
import HelperFunctions as Utils
-- default module definition
module Main where
-- custom module exporting everything
module Foo (..) where
-- custom module exporting only
-- the specified types and functions
module Bar (SomeType, someFunction) where
AKA the `R` in FRP
Signals are value that changes over time
type alias Person =
{ firstName : String
, lastName : String
, birthDate : String
}
john : Person
john =
{ firstName = "John"
, lastName = "Doe"
, brithDate = "1980-01-01"
}
$ elm package install
Some new packages are needed.
Here is the upgrade plan.
Install:
elm-lang/core 3.0.0
Do you approve of this plan? (y/n) y
Downloading elm-lang/core
Packages configured successfully!
$ elm-package install evancz/elm-html
To install evancz/elm-html I would
like to add the following
dependency to elm-package.json:
"evancz/elm-html": "4.0.2 <= v < 5.0.0"
May I add that to elm-package.json for you? (y/n) y
Some new packages are needed. Here is the upgrade plan.
Install:
evancz/elm-html 4.0.2
evancz/virtual-dom 2.1.0
Do you approve of this plan? (y/n) y
Downloading evancz/elm-html
Downloading evancz/virtual-dom
Packages configured successfully!
$ elm-package diff evancz/elm-html 3.0.0 4.0.0
Comparing evancz/elm-html 3.0.0 to 4.0.0...
This is a MAJOR change.
------ Changes to module Html.Attributes - MAJOR ------
Removed:
boolProperty : String -> Bool -> Attribute
stringProperty : String -> String -> Attribute
------ Changes to module Html.Events - MINOR ------
Added:
type alias Options =
{ stopPropagation : Bool, preventDefault : Bool }
defaultOptions : Html.Events.Options
onWithOptions : String -> Html.Events.Options
-> Json.Decode.Decoder a
-> (a -> Signal.Message) -> Html.Attribute
import Html.Events exposing (onClick)
type alias Model = Int
init : Int -> Model
init count = count
type Action = Increment | Decrement | Reset
update : Action -> Model -> Model
update action model =
case action of
Increment ->
model + 1
Decrement ->
model - 1
Reset ->
0
import Html.Events exposing (onClick)
type alias Model =
Int
init : Int -> Model
init count =
count
type Action
= Increment
| Decrement
| Reset
update : Action -> Model -> Model
update action model =
case action of
Increment ->
model + 1
Decrement ->
model - 1
Reset ->
0
tests : Test
tests =
suite "A Test Suite"
[ test "Addition" (assertEqual (3 + 7) 10)
, test "String.left" (assertEqual "a" (String.left 1 "abcdefg"))
, test "This test should fail" (assert False)
]
Successfully compiled TestRunner.elm
Running tests...
1 suites run, containing 3 tests
0 suites and 2 tests passed
1 suites and 1 tests failed
Test Suite: A Test Suite: FAILED
Addition: passed.
String.left: passed.
This test should fail: FAILED. not True
Online Editor
The Elm Architecture