When to use a sequence in F# as opposed to a list?
ListF#List Problem Overview
I understand that a list actually contains values, and a sequence is an alias for IEnumerable<T>
. In practical F# development, when should I be using a sequence as opposed to a list?
Here's some reasons I can see when a sequence would be better:
- When interacting with other .NET languages or libraries that require
IEnumerable<T>
. - Need to represent an infinite sequence (probably not really useful in practice).
- Need lazy evaluation.
Are there any others?
List Solutions
Solution 1 - List
I think your summary for when to choose Seq
is pretty good. Here are some additional points:
- Use
Seq
by default when writing functions, because then they work with any .NET collection - Use
Seq
if you need advanced functions likeSeq.windowed
orSeq.pairwise
I think choosing Seq
by default is the best option, so when would I choose different type?
-
Use
List
when you need recursive processing using thehead::tail
patterns
(to implement some functionality that's not available in standard library) -
Use
List
when you need a simple immutable data structure that you can build step-by-step
(for example, if you need to process the list on one thread - to show some statistics - and concurrently continue building the list on another thread as you receive more values i.e. from a network service) -
Use
List
when you work with short lists - list is the best data structure to use if the value often represents an empty list, because it is very efficient in that scenario -
Use
Array
when you need large collections of value types
(arrays store data in a flat memory block, so they are more memory efficient in this case) -
Use
Array
when you need random access or more performance (and cache locality)
Solution 2 - List
Also prefer seq
when:
-
You don't want to hold all elements in memory at the same time.
-
Performance is not important.
-
You need to do something before and after enumeration, e.g. connect to a database and close connection.
-
You are not concatenating (repeated
Seq.append
will stack overflow).
Prefer list
when:
-
There are few elements.
-
You'll be prepending and decapitating a lot.
Neither seq
nor list
are good for parallelism but that does not necessarily mean they are bad either. For example, you could use either to represent a small bunch of separate work items to be done in parallel.
Solution 3 - List
Just one small point: Seq
and Array
are better than List
for parallelism.
You have several options: PSeq from F# PowerPack, Array.Parallel module and Async.Parallel (asynchronous computation). List is awful for parallel execution due to its sequential nature (head::tail
composition).
Solution 4 - List
list is more functional, math-friendly. when each element is equal, 2 lists are equal.
sequence is not.
let list1 = [1..3]
let list2 = [1..3]
printfn "equal lists? %b" (list1=list2)
let seq1 = seq {1..3}
let seq2 = seq {1..3}
printfn "equal seqs? %b" (seq1=seq2)
Solution 5 - List
You should always expose Seq
in your public APIs. Use List
and Array
in your internal implementations.