Pernicious Myth No. 1: The Container Fiction

Blog » Pernicious Myth No. 1: The Container Fiction

Posted on

Newcomers to Haskell quickly learn that lots of containers are involved. Lists and tuples are containers; a monad is like a container; constructed data contain the arguments used in construction, and so forth.

It's a useful fiction, but it is a fiction. A value of type "[a]" does not contain values of type 'a', or anything else, for that matter. The Container Fiction is pernicious insofar as it retards progress toward a deeper understanding of how the notation works and why category theory provides such a powerful semantic framework.

All values are ontologically primitive.

This is obvious if you look at "simple" values like integers. Is 3 primitive? Some mathematicians would disagree. The number three, they might argue, is constructed using {} and a successor function. Or they might argue that it's a sequence of lambda expressions of some kind. There are probably lots of other ways to describe the meaning of '3' using various mathematical constructions.

These are all just different ways of describing a mathematical object; but as one of my college professors (expert in comparative religion) was fond of saying, map is not territory. Description of an object does not an object make. If these various and sundry descriptions all describe the same thing, then that thing must be independent of the descriptions.

Constructive mathematics does not construct its objects, it describes them. It would be the height of hubris to suggest otherwise; after all, numbers do not depend on the exertions of mathematicians for their continued existence.

The vitality of the Container Fiction demonstrates the power of the Iconic Fallacy. Values do not contain, but syntactic expressions do. We think that an ordered pair (a,b) contains a and b. It doesn't, but the graphical form, using parentheses, strongly implies that containment is involved. This is the Iconic Fallacy: false inference about the nature of the thing signified drawn from the form of the signifier. An expression like [1,2,3] "looks like" a list of integers; it does not therefore follow that it is (or denotes) a list of integers. It would be more accurate to say it is a construction that uses a list of integers to denote a value. The expression "[1,2,3]" represents application of a morphism (the [] functor) to an argument; if its denoted value contains 1, 2, and 3, then does the denoted value of $\sqrt 4$ contain $4$?

Part of the problem is that "List" (i.e. []) is misnamed, implying that '[a]' is a list. It's named after the target end of the arrow; a proper name would be something like "TypToList", thus emphasizing that it is a morphism.

This may seem like so much philosophical splitting of hairs, but it's not. On the contrary, it correlates with one of the fundamental insights of category theory, namely, elements don't matter. I believe it's very important for learners of Haskell (or any FPL with lazy evaluation) not only to learn to think in terms of mathematics, but also to learn to think about how mathematical notation works. I speak from experience; when I first picked up Haskell I used a book that insisted that something like "x = Foo 1 (2,3) 'a'" means that x contains 1, and (2,3), and 'a'. That didn't match up with the ideas I had formed about algebraic data types, so I eventually ended up dropping the whole thing in exasperation.

More specifically: it's important (essential?) to realize that we can only talk about values indirectly. On the one hand that means we have to use a language with syntax, obviously. But it's deeper than that. We can only talk about mathematical objects by "triangulating" using other, related mathematical objects. As a concrete example consider an ordered pair (1,2). Disregarding syntax, lambda expressions, etc., the value (1,2) is a primitive object that is the image of 1, 2 under the mapping ( ). The only way we can talk about it is to use these three things. Not the "contents" of (1,2) - it has no contents - but the things we used to construct (describe) it , which are completely distinct from the thing itself.

The most important reason to learn to think like this is that it is the primary idiom of category theory. For example, in CT terms, something like (1,2) is treated as an opaque object together with two projection arrows fst and snd. No contents involved, just an object and two mappings. The internal structure of the object is irrelevant. Saunders MacLane says somewhere that CT is about "learning to live without elements"; applied to Haskell, that means learning to live without containers.

Like this entry?