Lists are one of the core data structures in Elm. Elm supports lists on the syntactical level and the List core module has the usual basic utilities you would expect from a functional language. In this post, we take a look at lists in general and some of the useful functions from that module.
About This Series
This is the seventh post in a series of short and sweet blog posts about Elm . The stated goal of this series is to take you from “completely clueless about Elm” to “chief Elm guru”, step by step. If you have missed the previous episodes, you might want to check out the table of contents .
A great way to follow along and immediately try the code of this episode (instead of just reading it, which would probably be quite boring) is Elm’s REPL (read-eval-print-loop). With Elm installed , just start
elm-repl in the command line. You should see something like this:
>elm-repl ---- elm repl 0.16.0 ----------------------------------------------------------- :help for help, :exit to exit, more at --------------------------------------------------------------------------------
Now any Elm expression that you type will be evaluated immediately and the result is printed back to you. You probably wouldn’t want to develop anything complicated in the REPL but it’s great for playing around with some basic code snippets. So each time you see some code in this episode, try it out in the REPL and tinker with it. Have fun!
There are a number of ways to create lists in Elm. The most straight forward thing is to simply write down the elements, comma separated, between square brackets:
aList = [1, 2, 3, 4]
Result: [1,2,3,4] : List number
This looks a lot like arrays in C-Style programming languages, but Lists in Elm do not support positional access (you can not simply read/set the element at index n). Elm also has Array module, that offers positional access. But only lists are directly supported by syntactical elements so most of the times you’ll be working with lists.
As mentioned in the last episode, the actual type of a list always contains the type of its elements, that’s why the REPL infers the type
List number here (you could also annotate this as
List Int –
number is a supertype of
Of course you can build lists from any type of values, not just primitives like
Int, as long as all elements have the same type. Here’s a list of tuples for you:
[(1, 2, "three"), (4, 5, "six"), (7, 8, "nine")]
However, the following would raise a type error, because the second tuple has a different type than the first.
[(1, 2, "three"), (4, "five")]
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm The 1st and 2nd elements are different types of values. 3│ [(1, 2, "three"), (4, "five")] ^^^^^^^^^^^ The 1st element has this type: ( number, number', String ) But the 2nd is: ( number, String ) Hint: All elements should be the same type of value so that we can iterate through the list without running into unexpected values.
When lists get longer you can and should split their definition over multiple lines. The style most people are used to (and which works fine in Elm) would probably look similar to this:
aList = [(1, 2, "three"), (4, 5, "six"), (7, 8, "nine")]
However, a lot of Elm code (including the code in Elm core and several community packages) use a different style where the comma is at the start of the line:
aList : List (number, number, String) aList = [ (1, 2, "three") , (4, 5, "six") , (7, 8, "nine") ]
Of course, this is simply a matter of taste but it probably helps to have seen this style once so you know what’s up here.
(A remark for those of you who are following along with the REPL: Multiline expressions are possible in the REPL, though a bit of a hassle. End each line with a
\ and start all lines except the first with a space. Or just skip the REPL tinkering for the multiline code snippets.)
Another way to create lists is the dot notation. The following snippet creates a list of
Ints from 1 to 10.
[1,2,3,4,5,6,7,8,9,10] : List number
Last but not the least, in addition to the syntactical constructs to create lists you can also use functions from the List module.
List.repeat takes an integer
n and one arbitrary value and returns a list with
n copies of this value.
List.repeat 4 "Elm"
"Elm","Elm","Elm","Elm"] : List String
While this section is called “List Manipulation” you can not actually manipulate an existing list. In Elm, everything is immutable. The functions to manipulate a list all create a new list and leave the original list unchanged.
The prepend operator
:: prepends an item to the start of the list:
1 :: [2, 3, 4]
[1,2,3,4] : List number
You can prepend multiple times in a row. Theoretically you could always start with an empty list and build your lists only by prepending elements:
1 :: 2 :: 3 :: 4 :: 
[1,2,3,4] : List number
There is no operator to add a single element to the end of a list. You can however, append a list to another list:
List.append [1, 2, 3] [4, 5]
[1,2,3,4,5] : List number
There is an infix operator
++ that is an alias for append:
[1, 2, 3] ++ [4, 5]
[1,2,3,4,5] : List number
So to add a single element to the end of a list you usually just wrap it in a list and use `append`/`++`. like this:
[1, 2, 3] ++ 
[1,2,3,4] : List number
Another way to build up a single list from smaller lists is the
concat function which takes a list of lists and concatenates all of the individual lists into one large list:
List.concat [ ["one", "two", "three"], ["four", "five"], ["six"], ["seven", "eight", "nine"] ]
["one","two","three","four","five","six","seven","eight","nine"] : List String
Classics of Functional Programming
Now that we know a few different ways to build and manipulate lists, let’s have a look at some of the classical list functions that go beyond that.
Where would functional programming be without a map function? Of course the
List module has one.
List.map takes a function and a list and applies the function to all elements in the list. The result is a new list in which each element is the result of the function, applied to the respective element in the original list. Here is an example (please execute
import String in the REPL before trying the example):
List.map (\ word -> String.length word) ["a", "ab", "abc"]
[1,2,3] : List Int
In this example, we applied the length function to all elements in the list of strings.
List.filter is a similar evergreen. It takes a function and a list and removes all elements from the list for which the function returns
False. The following snippet removes all negative numbers from the list.
List.filter (\ n -> (n > 0)) [-1, 3, -2, 7]
[3,7] : List number
There are a lot more useful functions in the list module. Just to name a few:
List.headretrieves the first element of a list.
List.tailreturns a new list where the first element has been dropped.
List.foldrreduce a list to a single element by combining the first two elements with a given function, then combining the result with the next element, and so on.
Best check the List module’s API docs what else it has to offer.
Finally, if the
List module from core does not have what you need, check out the community package
List.Extra for even more functional list goodness.
This concludes the seventh episode of this blog post series on Elm. Continue with the next episode , on import statements in Elm.
Your job at codecentric?
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.