(elm)troduction
An introduction to elm-lang.
http://slides.com/chrisbuttery/elmtroduction
Elm in a nutshell
Elm is a functional language that compiles to JavaScript.
Not a functional wrapper for JS - a standalone language.
Created by Evan Czaplicki in 2012
For building games and user interfaces.
Has influenced many new JavaScript libraries
Good parts
-
Inferred type system ( No runtime exceptions!)
-
Stability - The compiler protects us from ourselves
-
Immutable data structures
-
Virtual DOM (so fast!)
-
Compiles to JS or HTML/CSS/JS
-
The terse syntax is enjoyable - no { } or ;
-
The Elm Architecture makes developing apps fun
SPOILER! There's no bad parts.
What's in the box?
elm-repl
Experiment with elm on the command line.
elm-make
Build and compile your .elm files to JavaScript
elm-reactor
Build & serve locally
elm-package
Install and publish elm packages.
Literals & Conditionals
True : Bool
False : Bool
42 : number -- Int or Float depending on usage
3.14 : Float
'a' : Char
"abc" : String
{-- If statements (no brackets, no semicolons) --}
if powerLevel > 9000 then "WHOA!" else "meh"
if n < 0 then
"n is negative"
else if n > 0 then
"n is positive"
else
"n is zero"
Defining functions
-- a function with 1 param
isEven n =
n % 2 == 0
-- a function with 2 params + type annotation
sum : number -> number -> number
sum x y =
x + y
sayHello : String -> String
sayHello name =
"Hello, " ++ name ++ "!"
-- usage
sum 3 6
-- 9 : number
isEven(sum 3 6)
-- False : Bool
sayHello "Bruce"
-- "Hello, Bruce!" : String
Core libraries
// Lets take a list of names, capitalise them, and reverse the order!
// JavaScript
["Sally", "Bill", "Bob", "Jenny"].map(name => name.toUpperCase()).reverse();
// ["JENNY", "BOB", "BILL", "SALLY"]
{--
-- List core library functions:
-- map : (a -> b) -> List a -> List b
-- reverse : List a -> List a
-- |> This is a forward function applicator (kinda like chaining)
-- }
List.map (\v -> String.toUpper v) ["Sally", "Bill", "Bob", "Jenny"]
|> List.reverse
["Sally", "Bill", "Bob", "Jenny"]
|> List.map (\v -> String.toUpper v)
|> List.reverse
-- ["JENNY", "BOB", "BILL", "SALLY"]
Using the List core library to manipulate a data structure
The Elm Architecture (TEA)
A basic pattern for web-app architecture
model - view - update
import Html exposing (div, button, text)
import Html.App exposing (beginnerProgram)
import Html.Events exposing (onClick)
-- application entry point
main =
beginnerProgram { model = 0, view = view, update = update }
-- view function
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]
-- update function
type Msg = Increment | Decrement
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
Not everything has to be written in elm
Interoperability with HTML, CSS & JavaScript
Communicate between JS & elm via
incoming/outgoing ports
JS compilation is compatible with CommonJS & AMD
How to get started?
Take aways
Less tooling - Say goodbye to JS fatigue
Statically typed with a helpful compiler
Easy transition from JavaScript
A fun(ctional) way of thinking and problem solving
Learn more
Elm Slack ( I'm @butters - say hi )
Questions?
Welcome!
Elm 101.5 Workshop
What is Elm?
Elm is a purely functional,
declarative programming language
for UI development,
built on functional reactive properties.
Answer the
following questions
-
What is The Elm Architecture (TEA)?
-
How can we use TEA?
-
What about components?
...and hopefully ignite your curiosity!
Some Basic Syntax
Records
type alias X =
{ a : Int
, b : String }
foo : X
foo = { a = 3
, b = "wowzers" }
bar : X
bar = { foo | a = 5 }
bar == { a = 5
, b = "wowzers" }
Function Anatomy
foo : Int -> Int -> Int
foo x y = x + x + y
// scala
def foo(x:Int)(y:Int):Int = x + x + y
// es6
const foo = x => y => x + x + y
// c#
Func<int, Func<int, int>> foo = x => y => x + x + y;
Lambdas
(\x -> x * x)
// scala
(x:Int => x * x)
// es6
(x => x * x)
// es5
(function(x){ return x * x; })
// math
λx. x * x
++ Operator
4 |> foo == foo 4
4 |> (\x -> x + 4) |> toString == "8"
|> Operator
[1,2] ++ [3,4] == [1,2,3,4]
"hi" ++ " " ++ "there" == "hi there"
Curry
foo : Int -> String -> String
foo 3 : String -> String
foo 3 "bar" : String
TEA has 5 parts
2 Types
Msg
The Type of discrete events in our application.
Model
Our single source of truth.
In TEA all application state resides on a single Model.
3 terms (values)
init/model
The initial Model
of our application.
update
The core logic.
Update "steps" our application forward based on Msgs.
view
Display function.
The Html in the view may produce
Msg's that will then flow
through our application.
Let's take a fictitious election,
and see how we might model it using TEA.
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
Let's get started
What happened to Signals?
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!
What does all this buy us?
Code is deterministic, immutable,
referentially transparent,
and side effect free.
Purity
Run-time errors in
Elm are exceptionally rare.
(types and good design)
Safety
Since all Elm functions are pure,
testing behavior is trivial.
Testability
Elm applications
are fast and efficient.
Performance
Components are broken into small common abstractions, allowing for a high degree of re-use and control.
Modularity
Bad code is hard to compile.
Errors are helpful, and
sometimes absurdly helpful.
Developer Experience
Thank you everyone.
Fearless refactoring
with elm
by Daniel Bachler (@danyx23)
this presentation is available at:
slides.com/danielbachler/fearless-refactoring-with-elm
25 minutes
the language
the architecture
the infrastructure
my experience so far
my pain points with js
- No static typing means simple typos can lead to runtime errors
- Deep changes to data structures in non-trivial JS projects need increadible amounts of diligence and attention
- A big part of the unit & integration tests test really boring properties (does it correctly reject an undefined param, ...)
What is elm?
- a functional programming language with static type checking that is compiled to JS
- an FRP (functional reactive programming) runtime
- designed to be easy to learn and good for web frontend dev
- uses VDOM to efficiently update the DOM, similar to React
Elm, the language
- Pure functional
- Statically typed
- Beginner friendly
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"
super friendly and helpful compiler
some personal favourite features
Union types
-- 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
Non-nullable values
-- 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
all values are immutable
x = 1
x = 2 -- compile error
x = x + 1 -- compile error
y = x + 1 -- ok
static typechecking
addNumbers : Int -> Int -> Int
addNumbers a b =
a + b
birthYearToString : Person -> String
birthYearToString person =
...
- The compiler always knows if your types check out or not
- You can add Type Annotations so the compiler can tell you where you went wrong
- Elm is always strongly typed, whether you add annotations or not (unlike Typescript, Flow, ...)
- There is no "any" type like in TypeScript
All functions are pure
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
fearless refactoring
- Compiler nags until all refactoring bugs are fixed
- Say goodbye to "undefined is not a function"
- No runtime errors in Elm
Unit tests can focus on logic
- In Javascript, unit tests have to check for undefined etc
- In Elm, the type system takes care of all of this
- Unit tests in Elm can focus on behaviour and logic
- Pure functions are super nice to test (little mocking required)
The Elm Architecture
Very similar to React without local state + Redux
Diagram by Evan Czaplicki
The Elm Architecture
See also André Staltz' great overview of unidirectional UI aproaches
The Elm Architecture
-- 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
}
benefits
- Model is single source of truth
- Creating visual elements is done by running the Model through a pure view function
- Side effects (XHRs, ...) are modelled as Tasks, handled by the runtime, results come back into the app as actions
- Apps are well structured. All state modifications happen in the central update (or a function called from there)
- Easy to replay whole UI sessions ( example)
The infrastructure
elm make
elm repl
elm package
package manager
- Handles installation of packages
- And publishing (inkl updates)
- Automatically enforces SemVer (!!)
Interop with "native Javascript"
- Incoming Ports (send messages from JS -> Elm)
- Outgoing Ports (send messages from Elm -> JS)
- Native modules (modules that use native JS - need to pass native review to be whitelisted)
Personal experience
- Last spring, started work on a side project, a slideshow editor
- Chose React for rendering (only pure components) with Redux to manage state (single source of truth) + Immutable.js
- Many nice properties, but refactoring was still a pain. Also pretty verbose, immutable.js field access via strings, ...
- Evaluated Typescript, Flow
- Then took a closer look at elm and really liked it
+
- General experience was very pleasant
- Compiler is very helpful, many bugs can't even exist, code quality is higher
- Refactoring with joy and confidence -> makes you do it more -> makes the code better
- Community is friendly and helpful
- Elm is small enough to learn it quickly, but still a very good stepping stone to other functional languages, esp. Haskell
- Code reads well
-
- Some rough edges (e.g. I use a script that deletes the compiler intermediate files before every compile because it would occasionally miss changed files)
- Elm core team is small, some PRs to Docs etc linger for a long time (will hopefully get better with the new Elm Foundation)
- Almost no support for running outside the Browser (=no server side rendering, ...)
- Some trivial things in JS are a bit of a hassle in Elm, e.g. focusing a control (has to be done via port)
Would I do it again?
absolutely!
How to get started
- try.elm-lang.org and debug.elm-lang.org
- elm-lang.org - great docs, read "The Elm Architecture"
- Check out awesome-elm
- try it for one widget of your app or a small side project
- If you don't know any other functional languages, the first steps will be a bit rough ("What do you mean there are no loops?")
- But it is very beginner friendly and a small, well-designed language
- Used in production today by several companies
Should we do a hackathon style
meetup in Berlin?
Thank you!
by Daniel Bachler ( @danyx23 on twitter)
Rethinking Web Developement
ELM
Who am I?
Gabriel Grinberg
- Frontend dev @WixAnswers
- Elm e nthusiast
- Mozzie's proud owner -->
Disclaimer
- I'm not an Elm expert
- Very new to the F(R)P world
- This talk is heavily opiniated
Let's face it..
JAvascript Sucks
Poor design
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.
Lack of Static typing & analysis
AKA the reason we have the famous JS WAT talk
console.log([]/(Math.sqrt('Bob' + 2))); // NaN :(
No Module system
<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>
...
...
Mutability & Side Effects
function calculateTotalPrice(products) {
var totalPrice = products.reduce(function (currentTotal, product) {
return currentTotal + product.price;
});
products.push('bob');
initiateWorldWar4();
return totalPrice;
}
Null
State Management
Result:
Tools Fatigue
SO, how can we solve it?
Elm TO The Rescue
wait.. another tool?!
What is ELM?
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.
Show us!
Show us some more!
Amazing compiler
If it compiles, it runs. No errors.
Highly addictive
-
Static analysis
-
Type checking and inference
-
Informative error messages
Compiler Features
Compiler Demo
Say goodbye To devtools-debugging
High performance
Also, Elm's immutable objects are cached internally, leading to better memory management
Package Manager that enforces Semver (!!!)
It also enforces documentation for published packages
Reactive by design
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
REACTIVE examples
MOre examples
Elm Architecture
Time Traveling debugger
FP features
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
SIDE EFFECTS & tasks
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
OK, but I want to use super-cool-library-thingie.js in elm.
No probz.
JS INterop using ports
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
webdev is slowly CONVERGING TO ELM
So, why aren't You using it yet in production?
Functional programming is
SCARY
Beautiful
Elm Is Still Young, and unpopular
only a few companies use it in production
(Prezi, NoRedInk, CircuitHub, Gizra and a few more)
Probably that-library-you-really-need.js wasn't ported yet to elm
Final Words
- If you're new to FP, learn Elm!
- If Redux sounds like alien technology to you, learn Elm!
- If reactive concepts (RxJS) make your head twist, go learn Elm!
- If you wanna write better JS code, go learn Elm
- Once you all learn Elm, let's adopt Elm @Wix!
Questions?
References
Thanks!
The Elm language and ecosystem
Thomas Brisbout
Freelance JS developer
@tbrisbout
Elm Paris Meetup / 2016-03-23
Front-End in 2016
Elm
What is elm ?
- Pure functional
- Immutable
- Static Type System
- No runtime errors
- use Virtual-DOM
Yet another language that compile to JS ?
Simple example
import Html
main = Html.text "Hello World"
Get started
$ 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
>
Syntax
Basics
-- 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".
"""
Functions
Function Definition
-- 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"
Function Type
sayHello : String -> String
sayHello name = "hello " ++ name
Function Type
add x y = x + y
add : number -> number -> number
add x y = x + y
Functions in Elm take one argument
Functions are curried
add x y = x + y
addOne = add 1
addOne 2 -- returns 3
-- The following are equivalent
add 3 4
(add 3) 4
Piping: Chaining functions
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
Function composition
double x = x * 2
addOne x = x + 1
(double >> addOne) 2 -- 5
(double << addOne) 2 -- 6
Data Structures
Lists
-- 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]
Tuples
("elm", 42)
fst ("elm", 42) -- "elm"
snd ("elm", 42) -- 42
error = (404, "Not found")
Records
person =
{ firstName = "John"
, lastName = "Doe"
}
person.firstName -- "John"
.firstName person -- "John"
.firstName { firstName = "Jane" } -- "Jane"
Records
person =
{ firstName = "John"
, lastName = "Doe"
}
{ person |
firstName = "George" }
-- { firstName = "George"
-- , lastName = "Doe"
-- }
Records
person =
{ firstName = "John"
, lastName = "Doe",
, age = 22
}
canDrinkAlcohol {age} =
age > 18
canDrinkAlcohol person -- True
Type aliases
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
}
Union Types
type Direction =
North | South | East | West
type Answer = Yes | No | Other String
Conditionals
if key == 40 then
n + 1
else if key == 38 then
n - 1
else
n
-- By the way, there
-- is no "Truthy" of "Falsy"
Switch Case
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
Module System
Imports
-- 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
Exports
-- 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
Signals
AKA the `R` in FRP
Signals are value that changes over time
Mouse.position
Keyboard.presses
Tooling
elm-make
Compile elm to js
- Generate index.html by default
- elm-make --output elm.js Main.elm
Hard to find typos
type alias Person =
{ firstName : String
, lastName : String
, birthDate : String
}
john : Person
john =
{ firstName = "John"
, lastName = "Doe"
, brithDate = "1980-01-01"
}
elm-reactor
elm-package
Init Project
$ 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!
Install dependency
$ 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!
Diff in API
$ 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
Custom loaders
elm-oracle
elm-format
Autoformat code
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
elm-test
Unit test in Elm
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
How to learn
Online Editor
Official tutorial
The Elm Architecture
Great (paid) video course on Pragmatic Studio
Community
Who is using Elm (in Prod)
Who to follow
Questions ?
Copy of elm
By Dima Pikulin
Copy of elm
- 1,733