Before asking what problems does it help to solve?, it may be helpful to ask where does it come from?
But to answer the question, what problems does it help to solve?, the answer is: The problem of handling zero or more items as a collection of things.
If you now say: But we already have collections (or, in .NET, IEnumerable<T>
) for that!, that's only because you're already in a context where such abstractions exist.
Imagine, however, a world where none of that exists.
Lists, in the sense of a 'cons list', is a way to solve the problem of 'a multitude' from first principles.
If all you have is something close to lambda calculus, you need to figure out how to work with many objects in a consistent way. A cons list is a data type you can define from an F-algebra. As such, it's a fundamental data type - a bit like logic gates (like NAND gates) are fundamental to computer circuitry.
Particularly in Haskell, it also turns out to be so useful as a fundamental collection type that it fits more than 80 percent of normal use. In Haskell, you can often solve your problems with the built-in linked list ([]
) data type.
That it's so useful in Haskell, however, also stems from the fact that Haskell is lazily evaluated. Thus, in Haskell, all linked lists are lazy, which means that if you evaluate the head, the tail may remain 'unevaluated'.
In F# terms, that's actually closer to seq 'a
(IEnumerable<T>
), which is also (potentially) lazily evaluated.
Even so, F# still comes with a built in cons list - the list
type. Even though it's not lazily evaluated in F#, it remains quite useful.
In many ways, in F#, list
, array
, and seq
are fairly interchangeable, with seq
as the odd man out (because it can be lazy and/or infinite).
In practice, in F#, list
and array
are usually interchangeable, in that they come with the same affordances. What you can do with a list
, you can do with an array
, and vice versa.
They do, however, have slightly different UX and performance characteristics. The F# language slightly favours lists over arrays, in that if you want to create or pattern match on lists, you can use the []
syntax, whereas arrays require that you use [||]
.
Lists can also be more efficient in some algorithms. Many functional algorithms build lists via recursion. Cons'ing an element unto an existing list is a cheap operation, because you don't have to change the tail. You just update the list pointer to point at the new head.
For arrays, on the other hand, every time you want to append an element, you have to allocate an entirely new array and copy the old values over.
That's not to say that you can't use arrays efficiently. You certainly can - particularly if you can calculate the size of the array up front.
In F#, sometimes arrays are fine, and sometimes lists are better. Personally, when I code in F#, I find lists easier to wield than arrays or seq
s.