In Rich Hickey’s thought-provoking goto conference keynote “The Value of Values” at 29 minutes he’s talking about the overhead of a language like Java and makes a statement like, “All those interfaces kill your reuse.” What does he mean? Is that true?
In my search for answers, I have run across:
-
The Principle of Least Knowledge AKA The Law of Demeter which encourages airtight API interfaces. Wikipedia also lists some disadvantages.
-
Kevlin Henney’s Imperial Clothing Crisis which argues that use, not reuse is the appropriate goal.
-
Jack Diederich’s “Stop Writing Classes” talk which argues against over-engineering in general.
Clearly, anything written badly enough will be useless. But how would the interface of a well-written API prevent that code from being used? There are examples throughout history of something made for one purpose being used more for something else. But in the software world, if you use something for a purpose it wasn’t intended for, it usually breaks.
I’m looking for one good example of a good interface preventing a legitimate but unintended use of some code. Does that exist? I can’t picture it.
7
Haven’t watched the full Rich Hickey presentation, but if I understand him correctly, and judging from what he says about the 29-minute mark, he seems to be arguing about types killing reuse. He is using the term “interface” loosely as a synonym for “named type”, which makes sense.
If you have two entities { "name":"John" }
of type Person
, and { "name": "Rover" }
of type Dog
, in Java-land they probably cannot interoperate unless they share a common interface or ancestor (like Mammal
, which means writing more code). So the interfaces/types here are “killing your reuse”: even though Person
and Dog
look the same, one cannot be used interchangeably with the other, unless you write additional code to support that. Note Hickey also jokes about projects in Java needing lots of classes (“Who here has written a Java application using just 20 classes?”), which seems one consequence of the above.
In “value-oriented” languages, however, you won’t assign types to those structures; they are just values which happen to share the same structure (in my example, they both have a name
field with a String value) and therefore can easily interoperate, e.g. they can be added to the same collection, passed to the same methods, etc.
To sum up, all this seems to be something about structural equality vs explicit type/interface equality. Unless I missed something from the portions of the video I haven’t watched yet 🙂
11
He is likely referring to the basic fact that an interface can not be instantiated. You can not reuse
an interface. You can only implement code that supports it, and when you write code for an interface there is no reuse.
Java has a history of providing frameworks of many API(s) that take an interface as arguments, but the team who developed the API never implement a wide range of classes for you to reuse with those interfaces.
It’s kind of like a GUI framework that has an IWindow
interface for a dialog box, and then you can add IButton
interfaces for controls. Except, they never gave you a good Button
class that implements IButton
. So you’re left creating your own.
Abstracted frameworks that have a wide range of base classes providing core functionalities are more reusable, and that works best when those abstracted classes are accessible to those using the framework.
Java developers started doing this thing where their API layers exposed only interfaces
. You could implement those interfaces, but you could never reuse classes from the developer that implemented those interfaces. It’s kind of like a cloak and dagger style of API development.
5
I think slide 13 at his presentation (The Value of Values) helps to understand this:
Values
- Don’t need methods
- I can send you values without code
and you are fine
My understanding is, Hickey suggests that if I need to, say, double the value you sent to me, I simply write code looking like
MyValue = Double(YourValue)
You see, above code is the same, no matter what kind value you sent – sort of a perfect reuse.
Now, how this would look like in the language having objects and interfaces?
Doublable MyValue = YourValue.Double()
oh wait! what if YourValue
doesn’t implement Doublable
? not that it can’t be doubled, it may perfectly be but… what if there’s just no method Double
? (what if there’s a method called say TwiceAsMuch
?)
Uh oh we’ve got a problem. YourValue.Double
won’t work, it can’t be reused anymore. Per my reading of above slide, this is about what Hickey meant when he said, “All those interfaces kill your reuse!”
You see, interfaces assume that objects are passed around “along with their methods”, along with code that operates on these. To use objects, one need to understand how to invoke that code, what method to call.
When expected method is missing, there is a problem, even though semantically, desired operation makes perfect sense for an object. As stated in the presentation, values don’t need methods (“I can send you values without code and you are fine”), allowing to write code dealing with them in a generic manner.
Side note: notion of passing around code-less values somehow reminds me of a Flyweight pattern in OOP.
an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory… Flyweight objects are by definition value objects. The identity of the object instance is of no consequence therefore two Flyweight instances of the same value are considered equal…
Flyweight usages I’ve seen typically followed the same approach of stripping off the code (methods, interfaces) from objects and passing stuff around around as, well, code-less values, expecting that receiving code has means necessary to operate on these.
This feels pretty much as at the slide, “values don’t need methods. I can send you values without code and you are fine”.
12
In an (i.e. my) ideal world classes and interfaces would always describe behavior, but the fact is that all too often they really just end up describing data. Only yesterday I watched video of someone build a so-called BankAccount
class that was nothing more than a glorified int
(indeed it was much less useful than an int
, thus ‘killing’ the reuse I would’ve had had it simply been left as an int
), all in the name of ‘good’ design. The amount of code, sweat and tears wasted on continually reinventing convoluted representations of data is staggering; if you’re not using the data in a meaningful way then please just let it be.
Now, this is the stage where Rich Hickey is content to throw the baby out with the bathwater and say that we should all go live in the land of values (a neighbor to the kingdom of nouns). I think, on the hand, that OOP can and does promote reuse (and importantly discoverability, which I find lacking in functional programming) when employed judiciously. If you’re looking for an OOP principle that best captures this tension I think it might be http://c2.com/cgi/wiki?TellDontAsk (which of course is a close cousin of Demeter)
2
I know this is an old question, but I feel like I have to provide an answer because even the accepted one is not what Rich Hickey meant. What he is talking about is broader than types. For example, a String is a type in Java, but it would qualify as a value under what Rich is discussing because it is immutable (among other things).
Consider instead the idea of a Bob object created using a Person class in say, Java. It has methods like getName, getAge, getAddress etc. It may implement some interfaces as well, who knows. How do you deal with the Bob object in another system written in Java? How do you deal with it in a system written in another language? You would literally have to recreate the same methods, class, and interfaces in that language or system, and even then, those systems would not be interoperable.
You cannot hope to comprehend what the Bob object is except by poking it with methods one by one to glean some details, but never understanding the whole or being able to perceive it. When Rich Hickey was talking about “operational interface”, he did not mean literal interfaces that classes adhere to, but how you interact with the thing itself, which in the case of objects would be methods like getters.
Now compare this with Bob in the form of a map like so (we’ll use Clojure):
{:name "Bob", :age 45, :occupation "Software Engineer"}
That’s actual data. You can see the whole of it instantly. You can pass it into various functions, you can alias it (with objects you would need to snapshot/lock/copy in case it changes), you can do comparisons to see if it’s equal to some other data, you can send it over the wire, other languages can easily manipulate it because they all have some notion of ints, strings, maps, etc. If you stick to data like this, you can use generic functions that operate on it instead of methods which are specific to your type or interface. (and which don’t exist in other languages or systems!)
He followed up with an example, asking you to imagine that instead of asking for a web page and getting the value itself (the html string and assets) you had to individually ask for each thing (getHeader, getBody). How do you know that in the end you are receiving the page exactly as the author intended? What if something changes in between calls?
This is the problem with place oriented programming and imperative languages, where things are mutable. To sum up Rich’s talk, information systems should be focused on values rather than implementation specific constructs like objects, because values are universal and easy to extend, share, and preserve. Erasing and overwriting made more sense when resources were limited.