47

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.

GlenPeterson
  • 14,890
  • 6
  • 47
  • 75
  • 1
    Haven't watched/read the stuff (I've added "Stop Writing Classes" to my to-watch list :) ), but maybe they are arguing from a dynamic vs static typing angle? ...again? – Andres F. May 23 '13 at 22:56
  • o.O Application programming interface interfaces – Thomas Eding May 23 '13 at 23:02
  • Thanks for the links! I didn't find Jack Diederich's talk particularly illuminating (watch how he fails to answer the audience's genuine questions convincingly.. "uh, yeah, maybe in that case...". I did like he seems to be arguing for Functional Programming without even noticing it ;) ), but the _"Imperial Clothing Crisis"_ is very good and insightful. – Andres F. May 25 '13 at 18:44
  • 1
    MPO is that people who don't believe in reuse don't break things down into small enough units. A big thing that is built for one specific purpose can't be reused. However, small things usually have a small enough purpose that the small purpose is useful in more than one context. – Amy Blankenship May 26 '13 at 01:45
  • 2
    @AmyBlankenship I found the "Imperial Clothing Crisis" linked above to be insightful. The author considers "reuse" a false idol (something which hasn't been proven useful in practice, and also most people don't even understand it even though they use the word). He also doesn't consider libraries "reuse"; you _use_ a library, you don't _reuse_ it. He also considers designing something for reuse "a double-edged sword"; something that people usually consider a win-win situation but which really isn't: when you design something for reuse, it's always a compromise (e.g. you may lose in simplicity) – Andres F. May 26 '13 at 23:23
  • I think he was actually looking at the wrong side of the equation. By coding to interfaces, the _clients_ of those interfaces are able to be reused, instead of having to build special code for each case. – Amy Blankenship May 27 '13 at 13:00
  • @AmyBlankenship Right. I would add that reuse is not a goal nor a benefit of OO. Assembler provides all the reuse you can wish for already. The gain that OO provides is control of complexity. It sounds to me like the typical geezer's response to any modern tool. "I don't need that, in the old days we used to [...] and that was perfectly fine!". – Martin Maat Mar 22 '21 at 05:47

5 Answers5

34

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 :)

Andres F.
  • 5,119
  • 2
  • 29
  • 41
  • 3
    BTW, Jack Diederich's talk "Stop Writing Classes" seems unrelated to this topic, and is more about YAGNI and "don't write code until you need it, and then only write simple code". – Andres F. May 25 '13 at 22:47
  • 17
    `ERROR: Object doesn't have a property called "name"` is often the result of `value-oriented` languages, and the other problem is when you want to no longer call that property `name`. Good luck refactoring because there are likely hundreds of objects with a property `name` but not all are `Person` or `Dog`. – Reactgular May 25 '13 at 23:18
  • 2
    @MathewFoscarini Yeah, I don't necessarily agree with it, it's just my interpretation of what I think Hickey was saying :) I like types and static typing; I'm just starting to dislike Java. And my dislike is unrelated to `interface`s but to the mess that is the typical Java project. – Andres F. May 25 '13 at 23:24
  • 1
    Java is the programming language for those who prefer to think too much. It's one of the few languages that allows a developer to easily hide his attempts to over engineer a project. – Reactgular May 25 '13 at 23:34
  • "In 'value-oriented' languages you won't assign types to those structures" - I think you need to say "In dynamic 'value-oriented'..." Haskell and Scala are value-oriented, but their static type system gives them the exact problem you are describing. I think that the solution to this problem is not values so much as using maps to pass parameters to functions. Using immutable maps (values) is just safer. – GlenPeterson Jun 13 '13 at 12:03
  • @GlenPeterson Yes, you are right. Or rather, instead of "dynamic" (which _do_ assign "named" types to the value) I'd say "languages with structural typing", such as Clojure. Note that what you suggest, rather than a full-blown solution is actually a trade-off; what you gain in flexibility you lose in type safety (which is what static typing addresses!). BTW, I'm no expert and might have got the details wrong ;) – Andres F. Jun 13 '13 at 12:18
  • Why the downvote? – Andres F. Sep 30 '13 at 15:15
  • I wish languages and frameworks would define a compiler-generated naming convention for "structural" delegates, interfaces, and aggregates, such that if two loaded assemblies declare a "structural" type with the same members, each assembly could regard the other assembly's type as equivalent to its own. Presently, the only way for assembly A and B to share a type without either having access to the other would be for both to have access to the same outside assembly C containing that type. – supercat Aug 22 '14 at 19:26
  • Won't constructs like like traits/mixins in other object oriented languages solve the problem of the kind of reusability Rich is complaining about? – Amogh Talpallikar Jul 17 '17 at 05:53
  • Objects are not "True Nouns" in the Prolog sense / what we consider nouns in the real world because traditional classes requires everything to be hierarchical rather than relationally. Going beyond Abstract classes / interfaces, DuckTyping comes even closer, but we also need to extend DuckTyping to include only which attributes/methods exist or don't exist, but also specific values (i.e: a RedThing means color field is red. Also every method in an object should be a "noun" itself, not a black box which is possible with keyword arguments and purely functional programming. – aoeu256 Aug 09 '19 at 22:13
  • 1
    I did watch the whole talk, many times, over the past 6 years. His statement goes far beyond this answer and I cannot summarize it here; "all those interfaces kill your code reuse" is a theme running through the entire talk. In addition to this answer: copying the value is trivial; serializing the value is easy; sharing the value with other languages or programs is easy; comparing values is easy. When using interfaces, special code must be written for each interface to copy, serialize, share, compare, etc.. Hence, code reuse is killed. – Jason Aug 08 '20 at 18:25
29

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.

GlenPeterson
  • 14,890
  • 6
  • 47
  • 75
Reactgular
  • 13,040
  • 4
  • 48
  • 81
  • 4
    Thank you for this answer. I now feel like I understand the question *and* the answer :) – MetaFight May 24 '13 at 07:59
  • 2
    +1 I really appreciate your answer and it adds a fascinating layer of interesting information to this question. But I think Andreas F.'s answer is likely closer to the heart of what Mr. Hickey meant, so I accepted his instead. – GlenPeterson May 27 '13 at 18:00
  • @GlenPeterson no problem, I think he might be on the mark as well. – Reactgular May 27 '13 at 18:01
  • 1
    Well this and the accepted answer highlight two slightly different, but equally interesting, interpretations. I'm curious which one Mr. Hickey had in mind when talking about this.. – David Cowden May 28 '13 at 22:12
  • You cannot reuse an interface , but you can extends it (and give valuable version number) without changing old classes. You can also inherit from many interfaces to add new job for new classes, or do add new inheritence in old recompiled classes . You can also extends the class that implements this interface for new job. – cl-r Jun 13 '13 at 13:21
15

I think slide 13 at his presentation (The Value of Values) helps to understand this:

https://i.stack.imgur.com/LVMne.png


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".

gnat
  • 21,442
  • 29
  • 112
  • 288
  • 6
    Generics would pretty much take care of that problem. Doubling makes sense on some objects, but not on others. In the Go language, there is implicit interface implementation (a form of duck typing), so you don't have all those interfaces to worry about. On the other hand, you sorta have to know which object is going to be hit by your method signature; otherwise, you might get unexpected results. There are always tradeoffs. – Robert Harvey May 23 '13 at 23:09
  • 1
    An interesting take. Good answer! – maple_shaft May 24 '13 at 00:57
  • @RobertHarvey "you sorta have to know..." -- it looks fairly simple to me, just think of how (un)boxing works in Java / C#. Boxed objects are passed around as _values_, without carrying any interfaces along, assuming that receiving code will unbox these and operate as needed. That's pretty much the same as Hickey states, _"I can send you values without code and you are fine"_ – gnat May 24 '13 at 07:11
  • 2
    The flyweight pattern is not what Rich is talking about. As the second sentence of the article states, the purpose of the flyweight pattern is to conserve memory. Rich's approach does not seek to do that. –  May 24 '13 at 12:41
  • @MattFenwick that's for sure: _side note_ part are my observations. "Resembles me...", "I've seen..." etc – gnat May 24 '13 at 12:55
  • @MattFenwick as for the _purpose_, whatever it is, the way I've seen it work is just as I describe, objects are stripped into values and passed around... _without code_ – gnat May 24 '13 at 13:05
  • 7
    `MyValue = Double(YourValue)` doesn't make sense if YourValue is a String, an Address, a User, a Function, or a Database. Otherwise, your missing-method argument is a strong one. OTOH, accessor methods let you enforce various constraints so that your Values are valid, and that only sensible operations are used to produce new Values. If you later decide to separate the Address from your User and Company, accessor methods mean that you don't break all the clients of your code. So they can help reuse in the long-term even if they sometimes hinder it in the short-term. – GlenPeterson May 24 '13 at 13:24
  • @GlenPeterson my understanding is, values that can't be doubled (Address, User) are simply not being sent. Note String, Function, Database can probably be doubled... somehow. For the record, I don't evangelize this stuff, merely try to decipher his ramplings based on what I see in the slides. Myself, I feel pretty comfortable in the [Kingdom of Nouns](http://steve-yegge.blogspot.co.uk/2006/03/execution-in-kingdom-of-nouns.html) :) – gnat May 24 '13 at 13:40
  • @gnat, do you really want to write a (double) function that can duplicate an arbitrary database of arbitrary size, including stored procedures and triggers, handling Oracle, PostgreSQL, MongoDB, and Datomic and will still multiply times 2 if you pass it an int? We aren't trading evil interfaces for reusable magic. It's one set of problems vs. another. You've just pushed the problem of sensible behavior from some class that hopefully implements the method you want to some function that hopefully operates sensibly on your data. – GlenPeterson May 24 '13 at 14:11
  • @gnat "The wrong values simply do not get sent" is what I guess dynamic typing is all about. No surprise Hickey (and the Python guy) are dynamic-type-language guys... I much prefer some help from the compiler telling me "no, you cannot double that, it doesn't make sense". – Andres F. May 24 '13 at 15:20
  • 5
    (On the other hand, I do agree that in Java-land, the explosion of classes, interfaces and frameworks is a nightmare. The simplest "enterprisey" solution in Java is a mess of code. So I do take a valuable lesson from this question & answer, without necessarily agreeing with the dynamic typing stuff) – Andres F. May 24 '13 at 15:23
  • 1
    Sorry, but after carefully rereading the article and listening to the speech, I feel forced to conclude that there's nothing more than the most incidental of similarities between the flyweight pattern and what Rich was talking about ... I would love to be convinced otherwise though! :) ... I also can't find any support for the notions that flyweight objects *must* be "code-less values", or that use of "code-less values" *must* mean one's applying the flyweight pattern ... :( –  May 24 '13 at 16:13
3

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)

CurtainDog
  • 334
  • 1
  • 6
  • What do you mean by discoverability? Is it similar to [this](http://www.faqs.org/docs/artu/ch06s02.html)? –  Jun 27 '13 at 15:20
  • 1
    Yes, I think that touches on a lot of the main points. It's a subtle point but discoverability is a balancing act, making things too transparent is undesirable as well because you'll get a bad signal to noise ratio. – CurtainDog Jun 28 '13 at 03:51
1

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.

krzyzowiec
  • 19
  • 1