- EML is a functional language that compiles to Javascrtip
- Compete with projects like React
- Elm has strong emphasis on simplicity, ease of use and quality tooling
- Require ELM 0.17
elm-reactor
to run in the browserelm-package install
to install ELM packages
- No runtime errors - no
null
,undefined
- Friendly error messages
- Well-architected code
- Automatically enforced semantic versioning for all ELM packages
elm-repl
to start REPL
> "hello"
> "hello" ++ "world"
(++)
operator to put strings together
> 2 + 3 * 4
14
> (2 + 3) * 4
20
> 9 / 2
4.5
> 9 // 2
4
(/)
for floating point division(//)
for integer division
> isNegative n = n < 0
<function>
> isNegative 4
False
> isNegative -7
True
> isNegative (-3 * -4)
False
- Use spacess to apply the function
> if True then "hello" else "world"
"hello"`
> if False then "hello" else "world"
"world"
- Do not need any parentheses or curly braces
- Elm doesn't have a notion of truthiness
- numbers, strings and list cannot be used as boolean values
> over9000 powerLevel = \
| if powerLevel > 9000 then "It's over 9000" else "meh"
<function>
> over9000 42
"meh"
- Using backlash
\
to split things on to multiple lines in REPL - Best practice to bring the body of a function down a line
names = ["Alice", "Bob", "Chuck"]
List.isEmpty names
returnsFalse
List.length names
returns3
List.reverse names
returns["Chuck", "Bob", "Alice"]
List.sort
numbers = [1, 4, 3, 2]
double n = n * 2
List.map double numbers
- Can hold a fixed number of values
- Each value can have any type
- Common usage: when need to return more than one values
import String
goodName name =
if String.length name <= 20 then
(True, "name accepted")
else
(False, "name was too long; please limit to 20 characters"
point = { x = 3, y = 4 }
point.x
bill = {name = "Gates", age = 57 }
bill.name
- A record is a set of key-value paris - similar to objects in JavaScript
- Create using curly braces
{...}
syntax - By starting the variable with a dot, it means please access the field with the following name
.name
is a function that gets thename
field of the record.name bill
works too; same asbill.name
under70 {age} = age < 70
under70 bill
- Can do some pattern matching to make thing lighter
bill = { bill | name = "Nye"}
- No destructive updates - create a new record rather than overwriting the existing one
- Elm makes this efficient by sharing much content as possible
- Similar to objects in Javascript but some crucial differences
- You cannot ask for a field that doesn't exist
- No field will ever by
undefined
ornull
- You cannot create recursive records with a
this
orself
keyword
Elm encourages a strict separations of data and logic and the ability to say this
is primarily used to break this separation. It is a systemic problem in Object Oriented Languages that Elm is purposely avoiding
Records supports structual typing, it means Elm's records can be used in any situations as long as the necessary field exists - providing flexibility without compromising reliability
- Simple pattern for nestable components
- modularity
- code reuse
- testing
- Redux translate the Elm arthitecture into JavaScript directly
import Html exposing(..)
-- MODEL
type alias Model = { ... }
-- UPDATE
type Msg = Reset | ...
update: Msg -> Model -> Model
update msg model =
case msg of
Reset -> ...
...
-- VIEW
view: Model -> Html Msg
view model =
...
- Model the state of your application
- Update a way to update your state
- View a way to view your state as HTMl
- Buttons
- Text Fields
- Check Boxes
- Radio Buttons
- etc..
onInput
function takes one argument- in the example case,
Change
function which was created whenMsg
was declared
- in the example case,
- A command i a way of demanding some effect
- ie. Asking for random numbers
- Making an Http Request
- Anything where you are asking for some value and the answer may be different depending on what is going on
- A subscription lets you register that you are interested in something.
- ie. Geolocation changes
- Messages coming in on a web socket
- Subscriptions let you sit passively and only get updates when they exist
Commands and Subscriptions make it possible for Elm components to talk to the outside world.
-- MODEL
type alias Model =
{ ...
}
-- UPDATE
type Msg = Submit | ...
update: Msg -> Model -> (Model, Cmd Msg)
update msg model =
...
-- VIEW
view: Model -> Html Msg
view model =
...
-- SUBSCRIPTIONS
subscriptions: Model -> Sub Msg
subscriptions model =
...
-- INIT
init: (Model, Cmd Msg)
init =
...
update
noew returns more than just a new model- returns a nwe model and some commands you want to run
- the commands are going to produce
Msg
values that will get fed right back intoupdate
function
subscriptions
let you declare any event sources you declare any event sources you need to subscribe to given the current model. Just likeHtml Msg
andCmd Msg
, these subscriptions will produceMsg
values that get fed right into ourupdate
functioninit
now produces both a model and some commands like like the newupdate
.This lets us provide a starting value and kick off any HTTP requests or whatever that need for initialization.
Aside:
commands
andsubscriptions
are data. When you create a command, you do not actually do it. Same with commands.You hand commands and subscriptions to Elm to actually run them, giving elm a chance to log all of this information. In the end, effects-as-data means Elm can
- Hava general purpose time-travel debugger
- Keep the same input, same output guarantee for all Elm functions.
- Avoid setup/teardown phases when testing
update
logic.- Cache and batch effects, minimizing HTTP connections or other resources
All the nice gurantees and tools in Elm come from the choice to treat effects as data
Cmd.none
means I have no commands, do nothing
Http.get
to GET some json fromurl
Json.at ["data", "image_url"] Json.string
reads as- Try to get the value at
json.data.image_url
and should be a sring
- Try to get the value at
Task.perform
is to clarifying what to do with the result- First argument
FetchFail
is when the GET fails - Second argument is
FetchSuccess
for the GET success
- First argument