If not are functional lists actually used at all and why?
Given that you're interested in data structures that are frequently modified, about the only use case I can see for persistent (linked) lists are as a basis for implementing a stack data structure.
- The
push
operation adds a new value at the front of the list (i.e. it creates one new list element and reuses the remainder of the previously existing list).
- The
peek
or top
operation returns the first value in the list.
- The
pop
operation returns the list starting after the first element.
Lists as defined by you work well as a stack data structure because all modifications happen in the ideal place, you never have to rebuild any part of the existing list when a new element is added or removed. And stacks typically don't require iteration over all of its elements / traversal in the order in which they were added (back to front).
Apart from that, AFAIK lists are a less than optimal data structure, because when you want to edit it in any place other than at its front, you have to rebuild all elements up to the place where the change occurs. Worst case is appending an element at the "end" of the list. That's why tree-based structures are a better fit as a basis for persistent ("immutable") data structures: they can reuse more existing nodes, while usually, only the nodes must be replaced that lie along the path to the node where some change should occur. ("change" = deriving a new, different data structure from the unchanged original.)
If you don't need to modify your list often, and you mostly read its elements in-sequence (not in random order), it might just be enough, too. One advantage is that it has very little memory overhead compared to more complicated structures, such as trees.
Node of 'a * 'a node | Nil
[…] Is there a clean functional implementation of these lists that doesn't have this issue?
Obviously not, if your definition of "list" is the one you gave. But again (and this is perhaps a somewhat naïve suggestion), you could implement a "list" on the basis of some kind of self-balancing binary tree:
- Iterating over all elements in that "list" would mean traversing the tree in the order left child first, parent node, right child first.
- Appending at the end of the list would mean a change at the right-most leaf node; you would have to rebuild only the path from the root node to that right-most leaf node, everything else could be reused.
- The less deep the tree is, the less elements you are likely to have to change when you're modifying the tree. Thus my suggestion of a self-balancing binary tree.
But again, I'm not an expert, and my suggestion might be naïve. For an in-depth treatment of functional data structures, check out the resources referenced below.
Further reading: