Object Identity And The Ship Of Theseus
While giving software design workshops and in coaching gigs, a topic that seems quite hard for people to understand is object identity. Feels like the issues and challenges around making sure an object –and I use object here in general terms, not necessarily in an Object-Oriented context– is the one you think it should be are not so obvious until you have a problem that requires you to think about this topic.
Recently I’ve been trying to introduce the using a famous paradox: The Ship of Theseus:
The ship wherein Theseus and the youth of Athens returned [from Crete] had thirty oars, and was preserved by the Athenians down even to the time of Demetrius Phalereus, for they took away the old planks as they decayed, putting in new and stronger timber in their place, insomuch that this ship became a standing example among the philosophers, for the logical question of things that grow; one side holding that the ship remained the same, and the other contending that it was not the same. —Plutarch, Theseus
In summary, when the hero Theseus returned from his adventures the people of Athens kept his ship for centuries. As the parts of the ship decayed the population replaced them with new parts. The question is: if the ship had its original parts replaced over time is this still Theseus’ ship?
The Paradox in Code
Here is a simple implementation of this problem in C#:
var theShipOfTheseus = new Ship ();
for (int i = 0; i < NumberOfPlanksRequired; i++) {
theShipOfTheseus.PlacePlank (new Plank ());
}
for (int i = 0; i < 30; i++) {
theShipOfTheseus.AddOar (new Oar ());
}
IsThisTheShipOfTheseus (theShipOfTheseus); //returns true
GoKickSomeMinotaurAss (theShipOfTheseus);
IsThisTheShipOfTheseus (theShipOfTheseus); //returns true
//after the adventures
for (int i = 0; i < CenturiesAndCenturiesAndCenturies; i++) {
Thread.Sleep (OneCentury);
theShipOfTheseus.RemoveAPlank ();
theShipOfTheseus.PlacePlank (new Plank ());
}
IsThisTheShipOfTheseus (theShipOfTheseus); //returns ????
What should IsThisTheShipOfTheseus (theShipOfTheseus) return? To make it a bit more interesting, let’s do what was proposed by the philosopher Thomas Hobbes, as described by Roderick M. Chisholm and adapted to our scenario:
Consider this possibility, suggested by Thomas Hobbes: "If some man had kept the old [parts] as they were taken out, and by putting them afterwards together in the same order, had again made a [car] out them, this, without doubt, had also been the same numerical ship with that which was at the beginning; and so there would have been two ships numerically the same, which is absurd." Assuming, as perhaps one has no right to do, that each of the [parts] survived intact throughout these changes, one might well argue that the reassembled [car] is the [car] we started with. "After all, it is made up of the very same parts, standing in the very same relations [...]"
var theShipOfTheseus = new Ship ();
for (int i = 0; i < NumberOfPlanksRequired; i++) {
theShipOfTheseus.PlacePlank (new Plank ());
}
for (int i = 0; i < 30; i++) {
theShipOfTheseus.AddOar (new Oar ());
}
IsThisTheShipOfTheseus (theShipOfTheseus); //returns true
GoKickSomeMinotaurAss (theShipOfTheseus);
IsThisTheShipOfTheseus (theShipOfTheseus); //returns true
//after the adventures
var otherShip = new Ship();
for (int i = 0; i < NumberOfPlanksRequired; i++) {
var plankRemoved = theShipOfTheseus.RemoveAPlank ();
otherShip.PlacePlank(plankRemoved);
}
for (int i = 0; i < 30; i++) {
var oarRemoved = theShipOfTheseus.RemoveAnOar();
otherShip.AddOar(oarRemoved);
}
IsThisTheShipOfTheseus (theShipOfTheseus); //returns ????
IsThisTheShipOfTheseus (otherShip); //returns ????