Category Archives: Philosophy

New blog post: I demand smarter tools

So, I forgot that I’d been cross-posting here. I’ve written several posts on philosopherdeveloper.com since my last update here:

Our encapsulated world, Part 2

Last week I wrote a short post discussing the concept of encapsulation. It was a short little post, primarily because I ended up being too tired to write as much as I had wanted to.

Well, at the moment I am sitting in the Phoenix airport for a 2-hour layover with nothing else to do (and therefore no excuse). So let’s explore this topic a bit more.

I already described encapsulation as the process of conceptualizing a system of many interrelated parts as a single unit. In Gӧdel, Escher, Bach (or GEB), Douglas Hofstadter utilizes a different term to describe the same notion: chunking. The image he evokes is that of taking many small things and assembling them into a distinct “chunk” that can be studied and acted upon from outside—i.e., from a higher level—without requiring much (or any) knowledge of the aforementioned “many small things” within it. Hofstadter provides an excellent series of examples of this process in the realm of science, starting with the notion of a quark as one of the smallest such “interrelated parts” composing larger-scale systems (i.e., protons and neutrons) that we know of.

[O]ne does not have to know all about quarks to understand many things about the particles which they may compose. Thus, a nuclear physicist can proceed with theories of nuclei that are based on protons and neutrons, and ignore quark theories and their rivals. The nuclear physicist has a chunked picture of protons and neutrons–a description derived from lower-level theories. Likewise, an atomic physicist has a chunked picture of an atomic nucleus derived from nuclear theory. Then a chemist has a chunked picture of the electrons and their orbits, and builds theories of small molecules, theories which can be taken over in a chunked way by the molecular biologist, who has an intuition for how small molecules hang together, but whose technical expertise is in the field of extremely large molecules and how they interact. Then the cell biologist has a chunked picture of the units which the molecular biologist pores over, and tries to use them to account for the ways that cells interact.

These examples are easily mapped to completely different domains where similar “chunking” takes place. For instance, consider the structure of a human society. At perhaps the lowest level, we have individual people with very specific desires and concerns. Then individuals form into households, which in turn compose neighborhoods, which in turn make up towns. You don’t need me to explicitly continue the progression; in your mind you already see that this sequence can be carried further with cities, counties, states/provinces, etc. (surely there are concepts like this in just about any governed area, though the specific terms denoting them may be different).

And just as Hofstadter’s examples included objects (fields of study) as well as subjects (scientists practicing within these fields), so can we discuss groups as well as leaders when it comes to human society. Neighborhoods may or may not have councils that oversee their community facilities; for towns and cities there are mayors, for states there are governors, and so forth. And like a chemist whose primary focus is at the molecular level, and who doesn’t necessarily need to have a deep understanding of the interactions among protons and neutrons, a state governor maintains a “big picture” view of the affairs of his or her state, typically without spending a considerable amount of time investigating the specific low-level issues facing each individual town and city (maybe some, but likely only the major ones) within it.

Another way of putting this is that we could say a state governor has a “chunked” view of his or her state’s towns and cities. He or she might view each as a single unit—an encapsulation of the neighborhoods, which in turn are encapsulations of their many households and individuals—and be interested in high-level facts, such as which towns have high crime rates, which have budget issues, etc.

Perhaps not-so-coincidentally, GEB contains yet another example of encapsulation that deals with societies, but not human societies: ant societies (i.e., ant farms). Hofstadter gives a fascinating account of how ant farms can exhibit high-level meaning even as the individual ants within them have no concept of the “bigger picture” to which they belong. For example, if certain members of the ant farm locate food, they will emit signals which are detected by other nearby ants from the farm, who will follow these signals and converge on the site of the food. The ants will all then form into a sort of “team” whose purpose is to deliver the food to some location that needs it. Hofstadter refers to such a “team” of ants as a signal, for reasons which I probably couldn’t explain as well as this excerpt:

Let’s say a signal is moving along. As it goes, the ants which compose it interact, either by direct contact or by exchange of scents, with ants of the local neighborhoods which it passes through. The contacts and scents provide information about local matters of urgency, such as nest-building, or nursing, or whatever. The signal will remain glued together as long as the local needs are different from what it can supply; but if it CAN contribute, it disintegrates, spilling a fresh team of usable ants onto the scene.

What Hofstadter has done is proposed a way of looking at ant farms that encapsulates the movement of groups of ants within the colony as signals which travel from one part of the colony to another, much like signals in the brain which travel via neurons. Considering them in this way, it is possible to visualize the same activities of an ant farm from a “high level” without even imagining ants at all! The ants in this view are something like a medium through which messages can be carried, and their role could just as easiliy be played by neurons or any number of other things.

But anyway, back to humans. An unfortunate but well-known weakness of the hierarchical system of human societies is that each additional layer of government adds a notorious layer of corruption and inefficiency. I don’t know well enough to say whether the “corruption” part is universal, but it seems almost unavoidable that the “inefficiency” part is; when lower levels are reliant upon higher levels for assistance in solving a problem, there is overhead involved in utilizing the proper channels to reach the higher levels, gaining the necessary approvals, waiting for the applicable resources—so as not to fail too miserably in trying to outline sources of inefficiency here, I won’t even attempt to exhaustively list them all. But there are undoubtedly more.

This is a bit ironic, because often the people most qualified to solve problems at the low level are those who are intimately familiar with those problems; but they are not the ones with the authority or the resources to put solutions in place.

This warrants another one of my nerdy software parallels, if only because I think it’s illuminating to consider how similar human society is to a computer system in this respect.

The similarity is this: computer systems, like human societies, can be broken down into smaller and smaller components at lower and lower levels of detail. At arguably the lowest level are the actual machine code instructions understood by a particular processor; above this are very primitive constructs offered in low-level languages such as assembly which aggregate groups of instructions into logical units; then above this are slightly more elevated constructs in slightly higher-level languages such as C. As you move higher and higher in this progression, you proceed through more and more levels of encapsulation, with each level (often with its own corresponding higher-level set of languages) viewing each component in the level below as a single “chunk” without requiring in-depth knowledge of the inner workings of that chunk.

This is actually an extremely powerful strategy that we humans have discovered as a way of extending our abilities as a species far beyond the abilities of our individual brains.

Consider what I’ll call a “high-level software application” such as Microsoft Excel. This is a fairly advanced tool that requires a rather complicated system (namely, the Windows operating system) in order to be useful. This complicated system in turn utilizes drivers and software libraries in order to run properly; these drivers rely on hardware with specialized instruction sets; each hardware device in turn relies on the correct functioning of its constituent parts. This progression could almost continue forever. What’s worth noting is that, once again, we are describing a series of systems wherein an expert of one level does not have to understand the inner workings of the level below. That is, the development team responsible for Microsoft Excel did not necessarily need to understand the inner workings of Windows in order to get their work done; they merely needed to understand certain components of Windows, and how they could interact with those components (e.g., how to interact with the file system in order to save a spreadsheet to disk)—those chunks as it were.

A well-known trade-off required for all this encapsulation is that the further software gets from the actual hardware it is running on—that is, the more layers of encapsulation separating the software instructions from actual hardware instructions—the greater the cost to the software’s performance. Modern high-level languages such as Java or C# require entire virtual machines to be running for the sake of portability. Dynamic languages like Python and Ruby require interpreters that require even greater overhead. These layers upon layers introduce a certain kind of inefficiency just like the multiple tiers of government in human society introduce inefficiency.

I would keep going, but at this point I am quite exhausted; so it will have to wait for a future installment. As it stands, I think attempting to draw parallels among the fields of science, the levels of human society, ant colonies, and various programming languages was quite enough for one blog post—regardless of how rambling and incoherent that attempt may have been!

Our encapsulated world: Part 1

This begins a series of posts about a concept I find fascinating. I plan to write on this topic in the form of a series because, frankly, I can’t imagine covering all the ground I want to cover in one post. I have too little free time these days to write epic blog posts very often anymore, sadly. Maybe that’s a good thing, though, because I have so much on my mind about this subject that it could probably get pretty overwhelming if I crammed it all into one post. Hopefully I don’t put anyone to sleep.

What I want to write about is, in a word, encapsulation.

“Encapsulation” is a term software engineers use all the time. We use it to describe cases where a complex (or at least non-trivial) set of software components is “encapsulated” within a single component, allowing developers to use that component in a flexible, reusable way without always having to juggle around its constituent parts individually. Outside the world of software, it can have more or less the same meaning. Whenever you hear of “encapsulation,” the idea being described is that of taking a system of parts and conceptualizing it as a discrete unit, which makes it easier to interact with in a comprehensible way.


Some great examples exist in the realm of data structures. Generally, low-level programming languages (such as C) do not offer out-of-the-box components for representing the concept of collections of objects. At best, they provide a single, highly primitive construct: an array, or possibly even just a pointer (a location in memory), which can be used along with a length to denote a contiguous block of memory. This block can then in turn be used to store several pieces of like data (e.g., numbers or dates).

Using such constructs is tedious at best, hazardous at worst. Programmers have to worry about off-by-one errors, buffer overflows, dynamic allocation/deallocation of memory, and all kinds of other nightmarish things. But this isn’t even the important point. The important point is that you can do very little useful work with this very basic concept, without having to write thousands of lines of laborious code. Suppose you want to store data in something resembling a table, allowing lookups by unique key? What if you want to maintain a queue of items, to be processed in the order they are received? Or what about some sort of branching hierarchical structure? Good luck without encapsulation.

On the other hand, with encapsulation, it quickly becomes amazingly simple to do things that would otherwise seem impossible. If I give you an array and I say, “Now I want to insert an item in the middle of this array,” you have a frustratingly difficult task ahead of you.

Disclaimer: the code below is horrible. I refuse to claim otherwise!

template<typename T>
int insert_element(T** array, int length, int capacity, int index, T element)
{
	// Determine the location of the last element in the array.
	int lastPos = length - 1;

	// Check if the array contains as many elements as it can hold.
	if (lastPos == capacity - 1)
	{
		// Store a pointer to the old array for deallocation.
		T* temp = *array;

		// Allocate extra space for the incoming element.
		capacity *= 2;
		*array = new T[capacity];

		// Copy all the elements over from the old array.
		for (int i = 0; i < length; ++i)
		{
			(*array)[i] = temp[i];
		}

		// Deallocate the old array.
		delete [] temp;
	}

	// Shift every element over by one down to the insertion point.
	int insertionPos = index;
	for (int i = lastPos; i >= insertionPos; --i)
	{
		// Ugh... so much work!
		(*array)[i + 1] = (*array)[i];
	}

	// Insert the element.
	(*array)[index] = element;

	// Return the new capacity.
	return capacity;
}

Pretty gross, right? Now let’s try and perform the exact same task, this time with a very useful encapsulation of the above logic which performs pretty much exactly the same work while requiring a developer to write far less code: the std::vector<T> class.

template<typename T>
void insert_element(vector<T>* array, int index, T element)
{
	array->insert(array->begin() + index, element);
}

Hmm, 40 lines became 5 lines. How is that possible? Quite simply, that entire mess of code up top, comprising an array, a length, a capacity, etc., is all essentially wrapped or encapsulated—this concept goes by many names—inside the insert operation, which is just one of many operations supported by the std::vector<T> class.

So that’s one way of interpreting the word “encapsulation”: as a way of representing a detailed interdependent system of many parts as if it were a single entity.

There are many more layers of meaning to this word, which I intend to explore in future posts. At the moment I have run out of steam; but in the meantime, how many examples of encapsulation can you think of in everyday life? I honestly believe that the more you think about it, the more astounded you will become by how ubiquitous it really is.

Anyway, I’ve got plenty more to share over the coming days. Don’t you worry!

Help! I’ve been straw-manned!

I think we’ve all been straw-manned at one point or another.

A straw man

For those unfamiliar with the term, a straw man is basically a false representation of an opposing opinion, which one attacks in order to present the illusion of having defeated said opponent in an argument. I remember one of my philosophy professors in college explained the concept using the example of a political debate where one candidate is unable to attend and so a straw man is brought in as a substitute. This allows the candidate in attendance to make unfair or flat-out inaccurate characterizations of his opponent’s platform: “My opponent likes to pretend that…”, “My opponent would have you believe that…”, etc.

This is how the term straw man is generally understood and presented: as a mean-spirited, aggressive tactic for undermining another person’s viewpoint. But I think it’s actually not so different from the much more common, much less malevolent scenario where two people simply misunderstand each other.

Case in point: many moons ago, a user on Stack Overflow posed a seemingly harmless question asking users to list some confusingly-named methods in the .NET BCL—cases where, just by looking at a method, a developer might think it does one thing, when in fact it does another.

In other words, mislabeled stuff.

A confusing Microsoft/Apple logo

Now that's confusing!

There were plenty of great answers, which prompted a lot of good discussion (e.g., what should you call such and such a method, etc.); however, there was also a pretty heated argument about one answer in particular: my answer.

I said that the DateTime.Add method (and all the DateTime methods whose names start with the word “Add”) has a misleading name. You know, because the DateTime type is immutable, these methods actually return new values, yada yada yada.

For example:

DateTime d = DateTime.Now;
d.AddHours(1.0);           // This does nothing meaningful.
d = d.AddHours(1.0);       // THIS does something meaningful.

I suggested that the word “Add” implies mutability, primarily based on the precedent of such other methods as ArrayList.Add, List<T>.Add, and so on.

What I would have done is given these methods names starting with the word “Plus”: DateTime.Plus, etc. I feel that this word doesn’t imply mutability, just as the phrase “x plus 5” does not imply any modification of the value stored at x.

To be clear: I realize this is 100% subjective. Someone else could see the word “Add” and not think it implies mutability, which undercuts the premise of my answer completely for that individual. I won’t dispute that.

But a funny thing happened with that answer: to my surprise, not only did some users disagree with me, they really disagreed with me. And it all started with a comment by a user who shall remain nameless (unless you click on the link above and look at the comments yourself, where you can clearly see his name):

I can’t say that I ever really thought twice about this. DateTime is a value type; it really doesn’t matter what its methods are called, none of them could possibly be mutators.

Never mind that this conflates the concepts of value types and immutability (don’t get me wrong; I believe the user in question has these concepts very straight in his head, but it is a misunderstanding shared by quite a few .NET developers); the real problem occurred a few comments later:

Consider if C# had date literals like VB and you could write #5/14/2010#.AddDays(1). What would you expect this to do? I maintain that if people get this wrong, it’s their fault, not the Framework’s fault for having a “confusing” method name. If I say “take your date of birth and add 1 month”, that doesn’t imply that you should (or could) physically change the date on which you were born.

It’s about here in the discussion that a straw man started to enter the picture. Honestly I felt a bit like our exchange was beginning to look like this:

A Red Delicious apple with a sticker on it that says "Gala"

Gala

Me: Boy, that’s a misleading caption on that picture of an apple.
Him: Why? It’s obviously a Red Delicious.
Me: Yeah, but… the caption says Gala.
Him: So what? It’s deep red; the skin is shiny and smooth; it is in every way like a Red Delicious!
Me: If that’s really your argument, the caption could just as well say “Orange” and you would still think it’s not misleading.
Him: For it to say “Orange” would make no sense. What are you talking about?
Me: I know it wouldn’t; that’s my point: it’s a bad name.
Him: I’m sorry, but you’d have to be stupid to think that’s a Gala.
Me: I never said I thought it was a Gala. I said it was a misleading caption.

Now, I don’t believe this user had any particularly strong interest in arguing with me just for the sake of it (that would be kind of silly). Rather, this is what I think happened. When I said that the DateTime.Add method (and its kin) had a confusing or misleading name, it reminded this particular user of other developers he’s interacted with, who may not understand the difference between value types and reference types, or why value types should generally be immutable, and thus why you should not call DateTime.Add and expect the instance to be modified. And so as this user became increasingly agitated (or so it seemed to me), he was really arguing, not with me, but with those other developers of whom I reminded him simply by commenting on what I felt (and still feel) is a misleading name.

That is to say, the straw man in this case was perhaps not an imaginary figure, but in fact other people. And maybe the whole reason for the debate that ensued was not that the other user wanted to argue with me, but simply that the topic brought up associations in his mind, to which he had a strong negative reaction.

Or maybe I’m just wrong; I don’t know. But I still think those methods have misleading names.

A LINQ riddle

OK, this is inexcusably nerdy, but…

Are these two LINQ extension calls opposite?

bool allNumbersAreEven = numbers.All(x => x % 2 == 0);
bool noNumberIsEven = !numbers.Any(x => x % 2 == 0);

What do you think? It seems like a yes, doesn’t it?

The above two lines of code are logically similar to:

All fribbles are wibbles.
No fribble is a wibble.

Can both statements above possibly be true? In my personal opinion, no. But it does admittedly depend on your definition of “All.” Suppose there is no such thing as a fribble? Then certainly the second statement, “No fribble is a wibble,” must be true—no fribble is a wibble because no fribble is anything. But what about the first? “All fribbles are wibbles”? Seems kind of odd, doesn’t it?

I would argue that to speak of “All” of a nonexistent entity is pretty meaningless. And so I would expect the following to output False:

var numbers = new int[0]; // Empty set
Console.WriteLine(numbers.All(x => x % 2 == 0));

But: it outputs True. Very strange, if you ask me!

So the designers of the LINQ extension methods All and Any apparently felt that a statement about something that doesn’t exist is by default true. So I guess all fribbles really are wibbles… and all pluborgs are klugorgs… and all morbablobs are snorpaklods.

Overwhelmed by choice

Take a look at the following beautiful user interface:

A terrible UI filled with way too many options

Be sure you make an optimal decision!

(Don’t even try to figure out what this form is collecting input for.)

What’s wrong with that interface? I ask as if it isn’t obvious: there are way too many choices! Seriously, how can you expect a user to make a sensible decision when there are this many options to choose from?

This is just a basic principle of good user interface design: simplify decision-making for the user by limiting choice. It sounds kind of dark and 1984-esque, but in fact it is quite a humane approach. (Also, to clarify: I’m not suggesting removing choices, but rather making them “opt-in”; i.e., if the user wants to change/enable something, he/she can do so by seeking out that choice rather than being bombarded with countless options up front.)

Here’s the classic example of what I’m talking about:

The Google UI

Seriously, how easy is that? You are only asked to make one choice: what do you want to search for? Notice there’s an “Advanced search” link beside the main input field, allowing you to seek more choices if you want them.

Have you ever found yourself confronted with a set of choices, and it stresses you out? I know I have. What’s really strange is that often, in these situations, you would be objectively worse off without the choice; yet you would be happier if your options were fewer.

Take college, for example. Let’s say you graduated from high school at the top of your class. You applied to Harvard and Princeton and got into both. Sweet, right? Except no, it’s not so sweet; now you have to make the agonizing choice of which school you want to attend, meanwhile recognizing that whatever choice you make, there’s always going to be this nagging question in your mind: What if I had picked the other school? Did I make the right choice?

On the other hand, if only one school accepted you, then your choice is made for you. No agonizing decision-making for you to worry about! Problem solved.

Here’s what I think happens in our brains when we’re making choices: we do our best to consider all possible options, but if the number of options to consider gets out of hand, we get “overloaded” and end up making a choice we know may not be optimal. This is a matter of practicality as we don’t live forever and thus don’t have the time or mental energy to consider every option available to us.

You C.S. folks will appreciate this: what’s the algorithmic complexity of this decision-making process?

static void ConsiderDecision(Option decision, OptionSet options)
{
    if (options.IsSingleOption)
    {
        ConsiderSimpleChoice(decision, options.Single());
        return;
    }

    foreach (Option option in options)
    {
        ConsiderDecision(option.RemainingOptions);
    }
}

Yeah, that’s right: it’s large (O(n!)—that’s the same as the Travelling Salesman Problem!). With each additional choice we’re given, the mental strain we endure in striving to choose among the choices available to us increases factorially, not linearly. At a certain point our brains recognize the impossibility of considering every individual option (much like a chess master recognizes that he or she can’t possibly consider every individual sequence of moves leading to a checkmate), and this causes us to actually panic a little bit. We make a choice we know we can’t be 100% confident in and just hope for the best.

Not to get too melancholy, but this all reminds me of an excellent song I sometimes enjoy listening to by The Swoon, a very obscure band (seriously, I don’t even know if you can find them on Google). The track is called “Speak Soft”; it’s a deeply sad but beautiful (in my opinion) examination of the state of being lonely and not knowing what to do with your life. In particular the lyrics I have in mind are from the song’s last verse:

Houdini closed himself inside of a box.
He didn’t have a trick to spring the lock.
Off the stage the people watched the clock.
Prison could be a nice place to live,
the bars on the window like bars on a crib.
Freedom is the least desired gift to give.

Moral of the story for application developers: next time you’re designing a user interface, don’t get carried away with the options. It isn’t nice to your poor users’ brains.

Moral of the story for everyone: recognize your own decision-making limitations and don’t seek out options that don’t truly matter to you. Otherwise, you’re only doing yourself a disservice.