Awesome
<img src="http://elm-lang.org/assets/logo.svg" width="26"> Elm Cheat Sheet
Table of Contents
- Hello World
- Comments
- Modules
- Tools
- HTML Embedding
- Primitives
- Collections
- Functions
- Types
- Type Aliases
- Type Annotation
- Operators
- Control Statements
- Ports
Elm in a nutshell
- purely functional language
- statically typed
- no runtime exceptions
- outperforms most popular rendering libraries
- package manager
- built-in tooling
- HTML, CSS, JavaScript interoperability
- clean syntax
- I like frontend again...
Hello World
File HelloWorld.elm
:
import Html exposing (h1, text)
import Html.Attributes exposing (id)
-- Hello world example
main =
h1 [id "hw"] [text "Hello World!"]
Comments
-- Single line comment
{-
Multi-line comment
-}
Comments for package documentation
Modules
-- Defining a module that exports everything
module Mymodule exposing (..)
-- Export only specified entities
module Mymodule exposing (Type, value)
-- Export all or specific states of type
module Mymodule exposing
( Error(Forbidden, Timeout)
, Stuff(..)
)
type Error
= Forbidden String
| Timeout String
| NotFound String
Imports
-- qualified imports
import String -- String.toUpper, String.repeat
import String as Str -- Str.toUpper, Str.repeat
-- unqualified imports
import Mymodule exposing (..) -- Error, Stuff
import Mymodule exposing ( Error ) -- Error
import Mymodule exposing ( Error(..) ) -- Error, Forbidden, Timeout
import Mymodule exposing ( Error(Forbidden) ) -- Error, Forbidden
Tools
REPL
elm repl
/ elm-repl
Import required modules
> import List
Get function signature
> List.map
<function> : (a -> b) -> List a -> List b
> (++)
<function> : appendable -> appendable -> appendable
Elm expressions return resulting value and type
> 1 + 1
2 : number
Backslash \
is for multi-line expressions
> fn a b = \
| a + b
<function> : number -> number -> number
Compile
elm make
/ elm-make
# Default compilation
elm make HelloWorld.elm -> index.html
# Custom name
$ elm make HelloWorld.elm --output hw.js
# Multiple files
$ elm make HelloWorld.elm MyModule.elm --output hw.js
# With warnings
$ elm make HelloWorld.elm --warn
# To HTML
$ elm make HelloWorld.elm --output hw.html
Packaging
elm package
/ elm-package
Installation automatically adds dependencies
in package.json
.
# Install a package
$ elm-package install evancz/elm-html
# Specific version
$ elm-package install evancz/elm-html 1.0.0
# Diff two versions
$ elm-package diff evancz/virtual-dom 2.0.0 2.1.0
Comparing evancz/virtual-dom 2.0.0 to 2.1.0...
This is a MINOR change.
------ Changes to module VirtualDom - MINOR ------
Added:
attributeNS : String -> String -> String -> VirtualDom.Property
Documentation
Publishing a package requires well documented code.
module Documentation exposing
( Type
, value
, anyfinCanHappen
)
{-| Module level documentation
# Just a header, what to include below
@docs Type, value
# About Anyfin
@docs anyfinCanHappen
-}
import Random exposing (int)
{-| Type level documentation comment -}
type Type = Bool
{-|-}
-- Empty comment above
value : Int
value = 1 + 1
{-| More on anyfinCanHappen -}
anyfinCanHappen : Generator Int
anyfinCanHappen =
int 0 64
{-| This value is not exported, so isn't required -}
-- Use basic comment syntax
imNotExported =
"Don't need to comment me"
- Documentation comment starts
{-|
ends with-}
- Module documentation comes after module declaration, before the imports
- Functions are grouped into related sections by keyword
@docs <args>
and declared with Markdown - Each exported entity should have documentation comment on top of its declaration
Publish
Add README.md
otherwise publishing will fail<br/>
All packages start with initial version 1.0.0
<br/>
Versions all have exactly three parts: MAJOR.MINOR.PATCH
<br/>
PATCH
- the API is the same, no risk of breaking codeMINOR
- values have been added, existing values are unchangedMAJOR
- existing values have been changed or removed
elm-package.json
source-directories
- array of directories to look up for project source code inside the working directoryexposed-modules
- modules that exposed to a user after publishing, kind of interface to your internal API.
# elm-package.json
source-directories": [
".",
"SubOne",
"SubTwo"
]
"exposed-modules": [
"SubOne",
"Goodmodule",
"Module"
]
├── SubOne
│ └── SubOne.elm # Compiled and exposed
├── SubTwo
│ └── SubTwo.elm # Compiled, but unexposed
├── SubThree
│ └── SubThree.elm # Compiler won't see the source, unexposed to the users
├── README.md
├── elm-package.json
├── Goodmodule.elm # Compiled and exposed
└── Module.elm # Compiled and exposed
Publish
$ git tag -a 1.0.0 -m "initial release"
$ git push --tags
$ elm-package publish
Updating
$ elm-package bump
$ git tag -a 1.0.1 -m "secondary release"
$ git push --tags
$ elm-package publish
HTML Embedding
Running fullscreen
elm-make HelloWorld.elm
-> elm.js
<script type="text/javascript" src="elm.js"></script>
<script type="text/javascript">
Elm.Main.fullscreen();
</script>
Embed explicitly in a html element
<script type="text/javascript" src="elm.js"></script>
<script type="text/javascript">
var elmHolder = document.getElementById('hw-wrapper');
Elm.Main.embed(elmHolder);
</script>
Run without graphics
Elm.Main.worker();
Primitives
Numbers
Numeric types are Int
and Float
, number
represents both Int
and Float
:
> 1
1 : number
> 2.0
2 : Float
> truncate 0.1
0 : Int
> truncate 1
1 : Int
Strings
String types are Char
and String
> 'a'
'a' : Char
> "Hello"
"Hello" : String
Multi-line string
"""
Hello
World
"""
Single quotes are for char
only
> 'ab'
-- SYNTAX PROBLEM --
> "ab"
"ab" : String
Booleans
> True
True : Bool
> False
False : Bool
Other
comparable
- ints
, floats
, chars
, strings
, lists
, tuples
<br/>
appendable
- strings
, lists
, text
.
<br/>
Kind of dynamic types are represented as a
, b
, c
etc. meaning that you can pass any value, even functions
Collections
All values and data structures in Elm are immutable.
Lists
A list
holds a collection of related values separated by commas and enclosed in
square brackets. All the values in a list must have the same type:
> []
[] : List a
> [1,2,3]
[1,2,3] : List number
> ["a", "b", "c"]
["a","b","c"] : List String
Ways to create a list
> List.range 1 4
> [1,2,3,4]
> 1 :: [2,3,4]
> 1 :: 2 :: 3 :: 4 :: []
Tuples
Tuples package many expressions into a single expression. They have a minumum of two elements and maximum of nine. The type of a tuple records the number of components and each of their types.
> (1, "2", True)
(1,"2",True) : ( number, String, Bool )
It's also possible to use commas as a tuple function, like a prefix operation.
> (,,,) 1 True 'a' []
(1,True,'a',[]) : ( number, Bool, Char, List a )
Destructuring
(x, y) = (1, 2)
> x
1 : number
Records
A record
is a collection of key/value pairs,
similar to objects in JavaScript or dictionary in Python
myRecord =
{ style = "Blue",
number = 1,
isCool = True
}
Accessing records
> myRecord.style
"Blue" : String
> .style myRecord
"Blue" : String
Updating records returns a new record
> updatedRecord = { myRecord | style = "Red", number = 10, isCool = False }
> myRecord.style
"Blue" : String
> updatedRecord.style
"Red" : String
Destructuring
{ style, number, isCool } = myRecord
> style
"Blue" : String
Extensible records
-- good usecase for extensible records is to be able to compose records and to be able to define functions that work on different kinds of records
-- define a type of record that has some properties and could have other properties
type alias Mammal r = {
r | legs : Int
}
-- cat is an example of extending the record Mammal by adding properties name and ageInHumanYears
cat : Mammal
{ name : String
, ageInHumanYears = 13
}
cat =
{ name = "Mittens"
, ageInHumanYears = 13
, legs = 4
}
-- mutate is a function that can work on any Mammal
mutate : Mammal r -> Mammal r
mutate mammal =
{ mammal | legs = mammal.legs + 1 }
mutatedCat = mutate cat
mutatedCat.legs == 5
Other
Core library also has:
Functions
Basics
-- function name | arguments names = function body
sum a b = a + b
-- combine arguments in a tuple
sum (a, b) = a + b
All functions in Elm are curried by default.<br/> If you have "a function of 2 arguments", it's really a function that takes one argument and returns a function that takes another argument:
-- Both are equal
myFunction arg1 arg2
((myFunction arg1) arg2)
-- Partial application
> minus x y = (-) x y
<function> : number -> number -> number
> minus1 = minus 1
<function> : number -> number
> minus1 11
-10 : number
Anonymous
Also known as lambdas
-- (\function arguments -> function body)
-- parenthesized, content starts with backslash
(\n -> n < 0)
(\x y -> x * y)
Prefix
Prefix notation is when we use operators as regular functions by enclosing them in parentheses.
-- Normally you would do this
> "abcde" ++ "fghij"
"abcdefghij" : String
-- Prefix
> (++) "abcde" "fghij"
"abcdefghij" : String
Types
Union Types
Elm allows to create custom types known as union types.<br/> The expression below creates a type which can have one of the values (or tags) from the right. Union types tightly coupled with case-of statement.
type Movement = Right | Left | Stop
Tags bring additional information, after tag itself comes a type or multiple types.
type Movement
= Right Int
| Left Int
| Stop Bool
| Coordinates (Float, Float)
-- passing to the function
myFunction ( Coordinates (45.7, 67.5) )
Union types can also have type variables
type Person a
= Name String
| Surname String
| Age Int
| About a
Maybe
A Maybe
can help you with optional arguments, error handling, and records with optional fields. Think of it as a kind of null
-- Maybe resides in a module
import Maybe exposing ( Maybe(..) )
-- Takes an argument that can be filled with any value
type Maybe a = Just a | Nothing
Type annotation explicitly tells that it will give back an Int
or it won't.
getId : Int -> Maybe Int
getId id =
if id >= 0 then
Just id
else
Nothing
Type Aliases
You can give existing types a custom name with type alias
type alias Name = String
type alias Dob = String
type alias Record = { name: Name, dob: Dob }
We can use it later annotating function
record : Record
record =
{ name = "Dave", dob = "27/08/1999" }
But still type alias
equals to it's parent type
type alias Name = String
name : Name
name =
"Dave"
secondName : String
secondName =
"Dave"
-- True
name == secondName
Type Annotation
Elm, like most ML dialects, automatically infers most types.<br/>
-- function name : 1st arg type -> 2nd arg type -> return type
fnc : Int -> List -> Int
Example below is read as function that takes an a value and returns a b value, list of a values returns a list of b values
map: (a -> b) -> List a -> List b
Pattern matching on record fields
-- Requires the argument with x and y fields
multiply {x,y} =
x * y
Annotating records
coordinates : { x : Float, y : Float }
coordinates =
{ x = 0,
y = 0
}
Operators
In a nutshell Elm operators are functions that take two arguments.
Arithmetic
Operator | Description | Type hint |
---|---|---|
+ | addition | number -> number -> number |
- | subtraction | number -> number -> number |
* | multiplication | number -> number -> number |
/ | floating point division | Float -> Float -> Float |
// | integer division, discard the reminder | Int -> Int -> Int |
^ | exponentiation | number -> number -> number |
% | modulo | Int -> Int -> Int |
Bitwise
Operator | Description | Type hint |
---|---|---|
and | bitwise AND | Int -> Int -> Int |
or | bitwise OR | Int -> Int -> Int |
xor | biwise XOR | Int -> Int -> Int |
Comparison
Operator | Description | Type hint |
---|---|---|
== | equal | comparable -> comparable -> Bool |
/= | not equal | comparable -> comparable -> Bool |
< | less than | comparable -> comparable -> Bool |
<= | less than or equal | comparable -> comparable -> Bool |
> | greater than | comparable -> comparable -> Bool |
>= | greater than or equal | comparable -> comparable -> Bool |
Logical
Operator | Description | Type hint |
---|---|---|
&& | logical and | Bool -> Bool -> Bool |
|| | logical or | Bool -> Bool -> Bool |
not | logical not | Bool -> Bool |
Function Composition
Operator | Description | Type hint |
---|---|---|
<| | backward (pipe) function application f <| x == f x | (a -> b) -> a -> b |
|> | forward (pipe) function application x |> f == f x | a -> (a -> b) -> b |
<< | composes functions into one, arguments first applied to the function from the right side | (b -> c) -> (a -> b) -> a -> c |
>> | same as before except arguments first applied to the function from the left side | (a -> b) -> (b -> c) -> a -> c |
Other
Operator | Description | Type hints |
---|---|---|
++ | put appendable things together | appendable -> appendable -> appendable |
:: | add an element to the front of a list | a -> List a -> List a |
as | keyword that creates aliases for values (x, y) as t == t = (x, y) | a -> a |
Control statements
If
All the branches of an if need to match so that no matter which one we take, we get back the same type of value overall.
if a < 1 then
"It's zero"
else
"Non-zero"
-- Multi-line.
if y > 0 then
"Greater"
else if x /= 0 then
"Not equals"
else
"silence"
Elm does not have the notion of “truthiness”.<br/> The condition must evaluate to True or False, and nothing else.
> if 1 then "nope" else "nope again"
- TYPE MISMATCH --
Case-of
Case tries to match the value of type against patterns defined after the of
keyword
type User
= Activated
| Deleted
update state =
case state of
Activated ->
-- do something
Deleted ->
-- do again
In case of passing tags with additional properties, parameters are passed along with type checking
type User
= Activated Int
| Deleted (Int, String)
update state =
case state of
Activated value ->
-- do something with value
Deleted values ->
-- do something with values
update ( Activated 1 )
update ( Deleted (0, "gone") )
Let-in
let
allows you to define intermediate values.
let
x = 3 * 8
y = 4 ^ 2
in
x + y
let
helps simplify complex expressions
let
activeUsers = List.filter (\u -> u.state /= 1) model.users
in
{ model | user = activeUsers}
Ports
Ports are a general purpose way to communicate with JavaScript.
From JavaScript to Elm
-- declare that this module uses ports
port module Main exposing (..)
-- define port
port portName : (String -> msg) -> Sub msg
var main = Elm.Main.embed(div);
// send into port
main.ports.portName.send("Port value");
From Elm to JavaScript
port showPortName : String -> Cmd msg
function logName(name) {
console.log(name);
}
// subscribe to receive events
main.ports.showPortName.subscribe(logName);
// unsubscribe
main.ports.showPortName.unsubscribe(logName);
Type interoperability
JavaScript | Elm |
---|---|
Booleans | Bool |
Numbers | Int, Float |
Strings | Char, String |
Arrays | List, Array, Tuples (fixed-length) |
Objects | Records |
JSON | Json.Encode.Value |
null | Maybe Nothing |