Day 3
Connecting the Dots
So far we have been writing the code of our program without thinking too much about what it means. Today we are going to learn about
Values and names
Functions
Modules
Types
SVG lines
Before we begin, let’s reflect on the following.
Every Elm program is composed of three basic building blocks:
values
,
names
and
types
.
For efficiency we are going to play with these concepts in REPL (the Read - Evaluate - Print Loop). Go to online Elm REPL
elmrepl.cuberoot.in
.
It should show something like this
Elm Repl
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
>
Here you can enter snippets of Elm code and see it evaluated.
To
evaluate
means to compute the value of a given expression, e.g. expression
2 + 2
has a value of
4
. The process of figuring it out is called evaluation. That’s what computer programs do!
Let’s start with values. The simplest way to create a value is to literally type it in our code. Try it (type things after
>
and expect to see what follows):
Elm Repl
> 2
2 : number
> "Hello"
"Hello!" : String
>
These expressions (
2
and
"Hello!"
) are called
literals
, because their values are literally what they look like. The value of
2
is simply
2
! When you enter them, the REPL evaluates and prints their
value
(which in this case is very easy) and
type
(the text after
:
). We will get to types later.
Some value literals are simple, like numbers and strings. Some are complex. Previously we have touched the subject of lists. You can also type them literally, like this:
Elm Repl
> [ 1, 2, 5, 77, 2 ]
[1,2,5,77.6,2] : List number
First notice that a list literal starts with
[
and ends with
]
. Inside there are values called
elements of the list
. Each element is separated from others with a
,
(comma). Then notice that each element is a value on its own, but the whole list is also a value.
It can be called a
composite value
. It’s like the box of eggs. Each egg is a thing, but the box of eggs is also a thing (composed of the box and the eggs).
List can be empty:
Elm Repl
> []
[] : List a
or contain only one element:
Elm Repl
> [ "Hello" ]
["Hello"] : List String
Notice that a list with one element is not the same as the element itself, just like the box with one egg is not the same as an egg alone. Also an empty list is not nothing, just like an empty box of eggs is not nothing. It may be a major disappointment when you are about to make some pancakes, but it’s still a thing.
Now let’s focus our attention to
names
. If you have a value, you can give it a name.
Elm Repl
> kittens = 2
2 : number
> greeting = "Hello!"
"Hello!" : String
>
From now on you can refer to this value either literally or by its name:
Elm Repl
> kittens
2 : number
> 2
2 : number
The effect will be exactly the same - you will get a value back. So we see that there are at least two ways of getting values: by literally expressing them or referring to them by names given to them previously. In a composite value of a list, you can mix the two ways together:
Elm Repl
> [ 1, kittens, 3 ]
[1,2,3] : List number
A
function
is a special kind of a value. Let’s create a named function like this:
Elm Repl
> fun something = something ++ " is fun!"
<function> : String -> String
Here we gave a name
fun
to a function, that given
something
will produce a string saying that this
something
is fun! Let’s see how you can use it:
Elm Repl
> fun "Learning Elm"
"Learning Elm is fun!" : String
Above we applied the function (by calling its name:
fun
) to a literal value:
"Learning Elm"
. In return the function produced another value:
"Learning Elm is fun!"
. So a function takes a value and gives a value. But function itself is also a value.
Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
Alternatively you can think of functions as parameterized values. The value you get depends on what value you provide to it. Eggs can have different sizes . An egg painting machine would give you different painted eggs depending on what egg you put in it.
Don’t worry if the concept of a function is not very clear yet. Most people have difficulty here. You will see some practical examples of functions soon.
Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling its name and (3) by applying a function to another value.
Not every function can be applied to any value. You can add two numbers (like
2
and
5
in
2 + 5
), but you can’t add a number to a string (
2 + "Hello!"
- try it and see an error!). The system that governs what function can be applied to what value is called a
type system
and it plays important role in Elm programming.
Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably get jammed. But a smart machine would sound a low pitched buzz and simply refuse to take the carrot . That way a smart machine avoids making a mess and possibly damaging itself. The Elm’s type system is what makes your functions smart. It prevents you from putting a carrot into the egg painting machine!
Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows its value and the type, like here:
Elm Repl
> kittens
2 : number
Above
2
is the value and
number
is the type. Similar here:
Elm Repl
> fun "Learning Elm"
"Learning Elm is fun!" : String
The value is
"Learning Elm is fun!"
and the type is
String
.
Composite values like lists have composite types. For example here the type is
List String
.
Elm Repl
> [ "Hello", "Good bye" ]
["Hello","Good bye"] : List String
This means that it’s a list of strings. All the elements of this list are of type
String
. When talking about lists you have to tell that it’s a list and what is the type of its elements. Only this can be considered a fully specified type. Compare:
Elm Repl
> [ 1, 2, 3 ]
[1,2,3] : List number
This is a list of numbers. It’s easy to see - all the elements are numbers, just as previously they were strings. All the elements of a list must be of the same type. If you try mixing the types, Elm will refuse to build your program. Try:
Elm Repl
> [ 1, 2, 3, "Carrot" ]
-- TYPE MISMATCH ----------------------------------------------------------- elm
The 4th element of this list does not match all the previous elements:
7| [ 1, 2, 3, "Carrot" ]
^^^^^^^^
The 4th element is a string of type:
String
But all the previous elements in the list are:
number
Hint: Everything in the list needs to be the same type of value. This way you
never run into unexpected values partway through. To mix different types in a
single list, create a "union type" as described in:
<http://guide.elm-lang.org/types/union_types.html>
What’s the type of an empty list? Let’s see:
Elm Repl
> []
[] : List a
A list of
a
. Obviously
a
is not a type. This means that the type of the elements cannot be determined yet. This is signaled by the lowercase term where the type should be: lowercase
a
in contrast with the capitalized
String
.
Notice that the
number
(type of value
15
for example) is also lowercase. That’s because it’s not a concrete type! Elm has two concrete types for numbers:
Int
that can represent an integer number (1, 2, 3, 0, -122 etc.) and
Float
that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it’s not possible to tell if
2
is an
Int
or a
Float
. We will see some of the implications of this later.
Above I said that every value has a type and also that a function is a value on its own. Let’s try the following in REPL:
Elm Repl
> fun
<function> : String -> String
We have asked the REPL to tell as the value referenced by name
fun
. In response the REPL display the value as
<function>
. That’s because there is no simple way to represent the value of a function as text. Let’s focus our attention on the type (part after the colon
:
). It is
String -> String
. This means that the type is a function (we recognize it by the arrow symbol). What’s before the arrow is a type of the value that goes into the function (called the
argument of the function
). The type of the argument is
String
. That shouldn’t be a surprise. Remember how we have used the function before to produce the value:
Elm Repl
> fun "Learning Elm"
"Learning Elm is fun!" : String
The part expressed as
"Learning Elm"
is a
String
(we recognize it by the quotation around it) and that’s exactly what the function needs.
The part after the arrow symbol is the type that the function returns. It is also
String
. That also should not surprise us, since
"Learning Elm is fun!"
is also a
String
.
Not every function returns the same type as it takes. For example the function
String.fromInt
takes a value of type
Int
and returns a value of type
String
. Its type is therefore
Int -> String
. We read it as:
type of String.fromInt is a function from Int to String
. Let’s see it in action:
Elm Repl
> String.fromInt 10
"10" : String
That’s right! The value
10
is an
Int
, the value
"10"
is a
String
.
Elm Repl
> String.fromInt
<function> : Int -> String
But what if we want to say that a certain number (say,
232
) is fun? We have our
fun
function, but it takes a
String
and what we have is an
Int
. We can use the very same
String.fromInt
to first convert it into the
String
and then pass the value to the
fun
function, like this:
Elm Repl
> fun (String.fromInt 232)
"232 is fun!" : String
We had to put the
String.fromInt 232
expression in parentheses, so that Elm understands that value
232
is to be passed to
String.fromInt
function, instead of the
String.fromInt
being passed to
fun
(which would not work anyway, since
fun
takes values of type
String
, not
Int -> String
).
Alternatively we can use the pipe operator which looks like this:
|>
. It has the advantage that the transformations that happen first are on the left side and ones that happen later on the right. We can read our code left to right instead of inside out. Here is how it would look like:
Elm Repl
> 232 |> String.fromInt |> fun
"232 is fun!" : String
Now it’s more apparent that the value
232
is passed to the function
String.fromInt
, and whatever comes on the other end is passed to the
fun
function. We don’t need parentheses in this case.
But what are functions good for? They are very good for making similar values that depend on some parameters. If you have to repeat similar code many times to get similar, but slightly different results, try using a function.
Just like machines for painting eggs are useful if you have a lot of eggs to paint. If you only need one or two painted eggs, then it’s probably better to just get them painted. But if you have thousands, maybe it’s worth to build a machine for it.
We don’t have to create all the functions that we need. There are thousands of them already defined and we can use them by importing modules.

Exercise

Let’s look at our code and try to recognize some literal values, lists, names and functions. Here is how it should look after day 2:
Elm
1
module Main exposing (main)
2
3
import Element
4
import Svg
5
import Svg.Attributes
6
7
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20
, Svg.circle
21
[ Svg.Attributes.r "10"
22
, Svg.Attributes.cx "0"
23
, Svg.Attributes.cy "0"
24
, Svg.Attributes.fill "orange"
25
, Svg.Attributes.transform "rotate(72) translate(80)"
26
]
27
[]
28
, Svg.circle
29
[ Svg.Attributes.r "10"
30
, Svg.Attributes.cx "0"
31
, Svg.Attributes.cy "0"
32
, Svg.Attributes.fill "red"
33
, Svg.Attributes.transform "rotate(144) translate(80)"
34
]
35
[]
36
, Svg.circle
37
[ Svg.Attributes.r "10"
38
, Svg.Attributes.cx "0"
39
, Svg.Attributes.cy "0"
40
, Svg.Attributes.fill "lime"
41
, Svg.Attributes.transform "rotate(216) translate(80)"
42
]
43
[]
44
, Svg.circle
45
[ Svg.Attributes.r "10"
46
, Svg.Attributes.cx "0"
47
, Svg.Attributes.cy "0"
48
, Svg.Attributes.fill "maroon"
49
, Svg.Attributes.transform "rotate(288) translate(80)"
50
]
51
[]
52
]
53
|> Element.html
54
|> Element.layout
55
[ Element.width Element.fill
56
, Element.height Element.fill
57
]
58
59
Take your time. Here are some hints to help you:
1.
We import three modules. Their names are to the right of the
import
keyword.
2.
Literal values are numbers and quoted texts. In our code there are only string literals so try to find all texts in quotes (
"like this"
). I count 26 of them.
3.
Then there are 13 lists, 5 of them empty and 8 with elements. Some lists are nested in other lists!
4.
Names are given to values wherever you see a
=
sign. The name is on the left and the value is below and slightly to the right. In our code we are giving only one name and its value is quite complex. In fact the whole program is about computing this value. Try to find it.
5.
Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like:
fun "learning Elm together"
), or that stand to the left of the
|>
(pipe operator; like:
"learning Elm together" |> fun
). The expression to the right of the function (or left of pipe) is called
the argument
. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g.
Svg.svg
takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and all of them are prefixed with a name of the module that exposed them to us.
6.
Interestingly there is only one name exposed by a module that do not refer to a function. Try to find it!

Let’s make a function

Now we know what is what. Let’s consider where we have a repetitive code that we could turn into a named function. If you look at the code from a distance, you can see that there is one block that is repeated 5 times almost without a difference. I’m talking about this:
Svg.circle [ Svg.Attributes.r "10" , Svg.Attributes.cx "0" , Svg.Attributes.cy "0" , Svg.Attributes.fill "..." , Svg.Attributes.transform "rotate(...) translate(80)" ] []
This block of code is repeated five times in our code - once for each dot. Now I’m going to show you how to eliminate the repetition in few steps.
First let’s make them even more uniform by adding
rotate(0)
to the first block. It will help us later. The code should look like this:
Elm
1 - 7
unfold
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "rotate(0) translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20 - 59
unfold
Then let’s give the value produced by this block a name. We can call it the
dot
. Select the first block that you have just modified, everything between
[
and the
,
on line 20 and cut it with
command
+
x
. Then in the empty space left type
dot
.
Move your cursor to the end of the file and type
dot =
, press
enter
and paste the code you cut before with
command
+
v
. The code should now look like this:
Elm
1 - 7
unfold
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ dot
13
, Svg.circle
14
[ Svg.Attributes.r "10"
15
, Svg.Attributes.cx "0"
16
, Svg.Attributes.cy "0"
17
, Svg.Attributes.fill "orange"
18
, Svg.Attributes.transform "rotate(72) translate(80)"
19
]
20
[]
21 - 50
unfold
51
52
53
dot = Svg.circle
54
[ Svg.Attributes.r "10"
55
, Svg.Attributes.cx "0"
56
, Svg.Attributes.cy "0"
57
, Svg.Attributes.transform "rotate(0) translate(80)"
58
, Svg.Attributes.fill "skyblue"
59
]
60
[]
61
62
Click
COMPILE
. Everything should work exactly the same as before. One of the dots is now a named value, but for our program it makes no difference. What is important is its value, not where it comes from.
Of course the goal is to have our list of dots looking like this:
[ dot , dot , dot , dot , dot ]
Let’s try it. The effect will be that all the dots will be in the same place, one on top of another. Also they will all have the same color. So on the screen you should see only one dot, somewhat to the right of center.
The dots need to have some parameters so they can be different. We already identified them: color and rotation. Above I said that a function is a parametrized value. Let’s turn our dot definition on line
52
into a function with two parameters like this:
Elm
1 - 7
unfold
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ dot "skyblue" 0
13 - 51
unfold
52
53
dot color rotation =
54
Svg.circle
55
[ Svg.Attributes.r "10"
56
, Svg.Attributes.cx "0"
57
, Svg.Attributes.cy "0"
58
, Svg.Attributes.fill "skyblue"
59
, Svg.Attributes.transform "rotate(0) translate(80)"
60
]
61
[]
62
63
This way we declared that when calling a
dot
name you will provide two values: first one for
color
and second for
rotation
. Let’s stay true to our word and provide it. Change the list on lines
12 - 16
to look like this:
Elm
1 - 7
unfold
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ dot "skyblue" 0
13
, dot "orange" 72
14
, dot "red" 144
15
, dot "lime" 216
16
, dot "maroon" 288
17
]
18 - 23
unfold
24
25
dot color rotation =
26
Svg.circle
27
[ Svg.Attributes.r "10"
28
, Svg.Attributes.cx "0"
29
, Svg.Attributes.cy "0"
30
, Svg.Attributes.fill "skyblue"
31
, Svg.Attributes.transform "rotate(0) translate(80)"
32
]
33
[]
34
35
Let’s reload again and see that there is still only one, skyblue dot visible. That’s because we did not tell Elm what to do with the values of the two parameters we provide for the
dot
function. We gave them names (
color
and
rotation
), but never called them.
First, let’s use the
color
parameter. If you want to change the color of the dot, you need to pass a different value to the
Svg.Attributes.fill
function on line
30
. Currently the value is a literal string
"skyblue"
. Instead we want to give it whatever value was given for the
color
parameter. We do it by simply calling the name of the parameter in place of a literal value, like this:
Elm
1 - 7
unfold
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ dot "skyblue" 0
13
, dot "orange" 72
14
, dot "red" 144
15
, dot "lime" 216
16
, dot "maroon" 288
17
]
18 - 23
unfold
24
25
dot color rotation =
26
Svg.circle
27
[ Svg.Attributes.r "10"
28
, Svg.Attributes.cx "0"
29
, Svg.Attributes.cy "0"
30
, Svg.Attributes.fill color
31
, Svg.Attributes.transform "rotate(0) translate(80)"
32
]
33
[]
34
35
Now let’s do the same for rotation. This one is more complex, because we cannot just pass
rotation
value to the
Svg.Attributes.transform
function. The
rotation
is just a number, while the function expects a string formatted like this:
"rotate(...) translate(80)"
.
We can use the
++
operator to glue the strings together. First part is constant, so we can just give it a literal value of
"rotate("
. After that we put the
++
and our variable part:
rotation
. Finally the rest of the string is also constant:
" translate(80)"
(notice the space before
translate
!). All together we have:
Elm
1 - 23
unfold
24
25
dot color rotation =
26
Svg.circle
27
[ Svg.Attributes.r "10"
28
, Svg.Attributes.cx "0"
29
, Svg.Attributes.cy "0"
30
, Svg.Attributes.fill color
31
, Svg.Attributes.transform "rotate(" ++ rotation ++ ") translate(80)"
32
]
33
[]
34
35
But Elm will complain about this. First it looks like we gave 5 arguments to the
Svg.Attributes.transform
function, and it only takes one. We can fix it by putting a parentheses around this part:
"rotate(" ++ rotation ++ ") translate(80)" }
basically telling Elm that this is a single expression and it should evaluate it before passing its value to the
Svg.Attributes.transform
function. Now it should look like this:
Elm
1 - 23
unfold
24
25
dot color rotation =
26
Svg.circle
27
[ Svg.Attributes.r "10"
28
, Svg.Attributes.cx "0"
29
, Svg.Attributes.cy "0"
30
, Svg.Attributes.fill color
31
, Svg.Attributes.transform ("rotate(" ++ rotation ++ ") translate(80)")
32
]
33
[]
34
35
Here we face another problem. Elm complains with the following:
-- TYPE MISMATCH ------------------------------------------------ src/Main.elm The 2nd argument to `dot` is not what I expect: 12| [ dot "skyblue" 0 ^ This argument is a number of type: number But `dot` needs the 2nd argument to be: String Hint: I always figure out the argument types from left to right. If an argument is acceptable, I assume it is "correct" and move on. So the problem may actually be in one of the previous arguments! Hint: Try using String.fromInt to convert it to a string?
It’s the type system in action. We can only use
++
function with both arguments being the same type. Here one argument is:
"rotate(" : String
and another is:
rotation : Float
Elm won’t have that. Fortunately there is an easy way to satisfy the type system. The
String.fromFloat
takes a
Float
and gives a
String
. Try it in the REPL:
Elm Repl
> String.fromFloat
<function> : Float -> String
> String.fromFloat 10
"10" : String
> String.fromFloat 10.2
"10.2" : String
That’s what we need! Let’s pass the value of
rotation
through this function like this:
Elm
1 - 23
unfold
24
25
dot color rotation =
26
Svg.circle
27
[ Svg.Attributes.r "10"
28
, Svg.Attributes.cx "0"
29
, Svg.Attributes.cy "0"
30
, Svg.Attributes.fill color
31
, Svg.Attributes.transform ("rotate("
32
++ (String.fromFloat rotation)
33
++ ") translate(80)"
34
)
35
]
36
[]
37
38
This should work! Ellie should now present the dots as intended and I hope you agree that the code is more readable now.

Lines to Connect the Dots

Armed with our functional superpowers, let’s make another function that draws a line. Let me first show you a complete code and then we can discuss it.
Elm
1 - 36
unfold
37
38
39
line color rotation =
40
Svg.line
41
[ Svg.Attributes.strokeWidth "1"
42
, Svg.Attributes.x1 "0"
43
, Svg.Attributes.y1 "0"
44
, Svg.Attributes.x2 "80"
45
, Svg.Attributes.y2 "0"
46
, Svg.Attributes.stroke color
47
, Svg.Attributes.transform
48
(String.concat
49
[ "rotate("
50
, String.fromFloat rotation
51
, ")"
52
]
53
)
54
]
55
[]
56
57
It works similar to the
dot
function so you can just copy and paste it. Then apply the following changes:
1.
The name needs to be different.
line
seems appropriate.
2.
Instead of a
Svg.circle
we will call the
Svg.line
function
3.
The line doesn’t have a radius, so remove the first attribute,
Svg.Attributes.r
.
4.
The line goes from one point to another, so it has four coordinates
x1
and
y1
for the beginning,
x2
and
y2
for the end. Beginning should be in the middle of the circle, which is
x=0, y=0
(
the origin
). We will make the end reach to the big circle by giving the
x2
value of
80
(same as a radius of the circle).
5.
Line can’t be filled, as it has no area. Instead we give it a color using
Svg.Attributes.stroke
function.
6.
We do not need to translate the line. It’s enough to rotate it.
Once you have the function defined, let’s use it. For every dot, create one line, like so:
Elm
1 - 7
unfold
8
main =
9
[ dot "skyblue" 0
10
, line "skyblue" 0
11
, dot "orange" 72
12
, line "orange" 72
13
, dot "red" 144
14
, line "red" 144
15
, dot "lime" 216
16
, line "lime" 216
17
, dot "maroon" 288
18
, line "maroon" 288
19
]
20 - 68
unfold
You should see something like this on Ellie:
Congratulations!
This was the toughest day of the workshop. Now you are ready for
Day 4
!