Elm is a functional reactive programming language that compiles to (client-side) JavaScript. Elm is statically typed, meaning that the compiler catches most errors immediately and provides a clear and understandable error message. Elm is great for designing user interfaces and games for the web.
1-- Single line comments start with two dashes.
2{- Multiline comments can be enclosed in a block like this.
3{- They can be nested. -}
4-}
5
6{-- The Basics --}
7
8-- Arithmetic
91 + 1 -- 2
108 - 1 -- 7
1110 * 2 -- 20
12
13-- Every number literal without a decimal point can be either an Int or a Float.
1433 / 2 -- 16.5 with floating point division
1533 // 2 -- 16 with integer division
16
17-- Exponents
185 ^ 2 -- 25
19
20-- Booleans
21not True -- False
22not False -- True
231 == 1 -- True
241 /= 1 -- False
251 < 10 -- True
26
27-- Strings and characters
28"This is a string because it uses double quotes."
29'a' -- characters in single quotes
30
31-- Strings can be appended.
32"Hello " ++ "world!" -- "Hello world!"
33
34{-- Lists, Tuples, and Records --}
35
36-- Every element in a list must have the same type.
37["the", "quick", "brown", "fox"]
38[1, 2, 3, 4, 5]
39-- The second example can also be written with two dots.
40List.range 1 5
41
42-- Append lists just like strings.
43List.range 1 5 ++ List.range 6 10 == List.range 1 10 -- True
44
45-- To add one item, use "cons".
460 :: List.range 1 5 -- [0, 1, 2, 3, 4, 5]
47
48-- The head and tail of a list are returned as a Maybe. Instead of checking
49-- every value to see if it's null, you deal with missing values explicitly.
50List.head (List.range 1 5) -- Just 1
51List.tail (List.range 1 5) -- Just [2, 3, 4, 5]
52List.head [] -- Nothing
53-- List.functionName means the function lives in the List module.
54
55-- Every element in a tuple can be a different type, but a tuple has a
56-- fixed length.
57("elm", 42)
58
59-- Access the elements of a pair with the first and second functions.
60-- (This is a shortcut; we'll come to the "real way" in a bit.)
61Tuple.first ("elm", 42) -- "elm"
62Tuple.second ("elm", 42) -- 42
63
64-- The empty tuple, or "unit", is sometimes used as a placeholder.
65-- It is the only value of its type, also called "Unit".
66()
67
68-- Records are like tuples but the fields have names. The order of fields
69-- doesn't matter. Notice that record values use equals signs, not colons.
70{ x = 3, y = 7 }
71
72-- Access a field with a dot and the field name.
73{ x = 3, y = 7 }.x -- 3
74
75-- Or with an accessor function, which is a dot and the field name on its own.
76.y { x = 3, y = 7 } -- 7
77
78-- Update the fields of a record. (It must have the fields already.)
79{ person |
80 name = "George" }
81
82-- Update multiple fields at once, using the current values.
83{ particle |
84 position = particle.position + particle.velocity,
85 velocity = particle.velocity + particle.acceleration }
86
87{-- Control Flow --}
88
89-- If statements always have an else, and the branches must be the same type.
90if powerLevel > 9000 then
91 "WHOA!"
92else
93 "meh"
94
95-- If statements can be chained.
96if n < 0 then
97 "n is negative"
98else if n > 0 then
99 "n is positive"
100else
101 "n is zero"
102
103-- Use case statements to pattern match on different possibilities.
104case aList of
105 [] -> "matches the empty list"
106 [x]-> "matches a list of exactly one item, " ++ toString x
107 x::xs -> "matches a list of at least one item whose head is " ++ toString x
108-- Pattern matches go in order. If we put [x] last, it would never match because
109-- x::xs also matches (xs would be the empty list). Matches do not "fall through".
110-- The compiler will alert you to missing or extra cases.
111
112-- Pattern match on a Maybe.
113case List.head aList of
114 Just x -> "The head is " ++ toString x
115 Nothing -> "The list was empty."
116
117{-- Functions --}
118
119-- Elm's syntax for functions is very minimal, relying mostly on whitespace
120-- rather than parentheses and curly brackets. There is no "return" keyword.
121
122-- Define a function with its name, arguments, an equals sign, and the body.
123multiply a b =
124 a * b
125
126-- Apply (call) a function by passing it arguments (no commas necessary).
127multiply 7 6 -- 42
128
129-- Partially apply a function by passing only some of its arguments.
130-- Then give that function a new name.
131double =
132 multiply 2
133
134-- Constants are similar, except there are no arguments.
135answer =
136 42
137
138-- Pass functions as arguments to other functions.
139List.map double (List.range 1 4) -- [2, 4, 6, 8]
140
141-- Or write an anonymous function.
142List.map (\a -> a * 2) (List.range 1 4) -- [2, 4, 6, 8]
143
144-- You can pattern match in function definitions when there's only one case.
145-- This function takes one tuple rather than two arguments.
146-- This is the way you'll usually unpack/extract values from tuples.
147area (width, height) =
148 width * height
149
150area (6, 7) -- 42
151
152-- Use curly brackets to pattern match record field names.
153-- Use let to define intermediate values.
154volume {width, height, depth} =
155 let
156 area = width * height
157 in
158 area * depth
159
160volume { width = 3, height = 2, depth = 7 } -- 42
161
162-- Functions can be recursive.
163fib n =
164 if n < 2 then
165 1
166 else
167 fib (n - 1) + fib (n - 2)
168
169List.map fib (List.range 0 8) -- [1, 1, 2, 3, 5, 8, 13, 21, 34]
170
171-- Another recursive function (use List.length in real code).
172listLength aList =
173 case aList of
174 [] -> 0
175 x::xs -> 1 + listLength xs
176
177-- Function calls happen before any infix operator. Parens indicate precedence.
178cos (degrees 30) ^ 2 + sin (degrees 30) ^ 2 -- 1
179-- First degrees is applied to 30, then the result is passed to the trig
180-- functions, which is then squared, and the addition happens last.
181
182{-- Types and Type Annotations --}
183
184-- The compiler will infer the type of every value in your program.
185-- Types are always uppercase. Read x : T as "x has type T".
186-- Some common types, which you might see in Elm's REPL.
1875 : Int
1886.7 : Float
189"hello" : String
190True : Bool
191
192-- Functions have types too. Read -> as "goes to". Think of the rightmost type
193-- as the type of the return value, and the others as arguments.
194not : Bool -> Bool
195round : Float -> Int
196
197-- When you define a value, it's good practice to write its type above it.
198-- The annotation is a form of documentation, which is verified by the compiler.
199double : Int -> Int
200double x = x * 2
201
202-- Function arguments are passed in parentheses.
203-- Lowercase types are type variables: they can be any type, as long as each
204-- call is consistent.
205List.map : (a -> b) -> List a -> List b
206-- "List dot map has type a-goes-to-b, goes to list of a, goes to list of b."
207
208-- There are three special lowercase types: number, comparable, and appendable.
209-- Numbers allow you to use arithmetic on Ints and Floats.
210-- Comparable allows you to order numbers and strings, like a < b.
211-- Appendable things can be combined with a ++ b.
212
213{-- Type Aliases and Custom Types --}
214
215-- When you write a record or tuple, its type already exists.
216-- (Notice that record types use colon and record values use equals.)
217origin : { x : Float, y : Float, z : Float }
218origin =
219 { x = 0, y = 0, z = 0 }
220
221-- You can give existing types a nice name with a type alias.
222type alias Point3D =
223 { x : Float, y : Float, z : Float }
224
225-- If you alias a record, you can use the name as a constructor function.
226otherOrigin : Point3D
227otherOrigin =
228 Point3D 0 0 0
229
230-- But it's still the same type, so you can equate them.
231origin == otherOrigin -- True
232
233-- By contrast, defining a custom type creates a type that didn't exist before.
234-- A custom type is so called because it can be one of many possibilities.
235-- Each of the possibilities is represented as a "type variant".
236type Direction =
237 North | South | East | West
238
239-- Type variants can carry other values of known type. This can work recursively.
240type IntTree =
241 Leaf | Node Int IntTree IntTree
242-- "Leaf" and "Node" are the type variants. Everything following a type variant is a type.
243
244-- Type variants can be used as values or functions.
245root : IntTree
246root =
247 Node 7 Leaf Leaf
248
249-- Custom types (and type aliases) can use type variables.
250type Tree a =
251 Leaf | Node a (Tree a) (Tree a)
252-- "The type tree-of-a is a leaf, or a node of a, tree-of-a, and tree-of-a."
253
254-- Pattern match variants in a custom type. The uppercase variants will be matched exactly. The
255-- lowercase variables will match anything. Underscore also matches anything,
256-- but signifies that you aren't using it.
257leftmostElement : Tree a -> Maybe a
258leftmostElement tree =
259 case tree of
260 Leaf -> Nothing
261 Node x Leaf _ -> Just x
262 Node _ subtree _ -> leftmostElement subtree
263
264-- That's pretty much it for the language itself. Now let's see how to organize
265-- and run your code.
266
267{-- Modules and Imports --}
268
269-- The core libraries are organized into modules, as are any third-party
270-- libraries you may use. For large projects, you can define your own modules.
271
272-- Put this at the top of the file. If omitted, you're in Main.
273module Name where
274
275-- By default, everything is exported. You can specify exports explicitly.
276module Name (MyType, myValue) where
277
278-- One common pattern is to export a custom type but not its type variants. This is known
279-- as an "opaque type", and is frequently used in libraries.
280
281-- Import code from other modules to use it in this one.
282-- Places Dict in scope, so you can call Dict.insert.
283import Dict
284
285-- Imports the Dict module and the Dict type, so your annotations don't have to
286-- say Dict.Dict. You can still use Dict.insert.
287import Dict exposing (Dict)
288
289-- Rename an import.
290import Graphics.Collage as C
291
292{-- Ports --}
293
294-- A port indicates that you will be communicating with the outside world.
295-- Ports are only allowed in the Main module.
296
297-- An incoming port is just a type signature.
298port clientID : Int
299
300-- An outgoing port has a definition.
301port clientOrders : List String
302port clientOrders = ["Books", "Groceries", "Furniture"]
303
304-- We won't go into the details, but you set up callbacks in JavaScript to send
305-- on incoming ports and receive on outgoing ports.
306
307{-- Command Line Tools --}
308
309-- Compile a file.
310$ elm make MyFile.elm
311
312-- The first time you do this, Elm will install the core libraries and create
313-- elm-package.json, where information about your project is kept.
314
315-- The reactor is a server that compiles and runs your files.
316-- Click the wrench next to file names to enter the time-travelling debugger!
317$ elm reactor
318
319-- Experiment with simple expressions in a Read-Eval-Print Loop.
320$ elm repl
321
322-- Packages are identified by GitHub username and repo name.
323-- Install a new package, and record it in elm-package.json.
324$ elm package install elm-lang/html
325
326-- See what changed between versions of a package.
327$ elm package diff elm-lang/html 1.1.0 2.0.0
328-- Elm's package manager enforces semantic versioning, so minor version bumps
329-- will never break your build!
The Elm language is surprisingly small. You can now look through almost any Elm source code and have a rough idea of what is going on. However, the possibilities for error-resistant and easy-to-refactor code are endless!
Here are some useful resources.
-
The Elm website. Includes:
- Links to the installers
- Documentation guides, including the syntax reference
- Lots of helpful examples
-
Documentation for Elm’s core libraries. Take note of:
-
The Elm Architecture. An essay by Elm’s creator with examples on how to organize code into components.
-
The Elm mailing list. Everyone is friendly and helpful.
-
Scope in Elm and How to Read a Type Annotation. More community guides on the basics of Elm, written for JavaScript developers.
Go out and write some Elm!