Hacker News new | past | comments | ask | show | jobs | submit login
Execution in the Kingdom of Nouns (2006) (steve-yegge.blogspot.de)
94 points by ksdlck on Feb 13, 2013 | hide | past | favorite | 67 comments



It's nice to see this classic appearing on HN. Certainly worth a reread. (Edited, thx tikhonj)

The first time I read this article was about one year after leaving University. I graduated as a near pure Java developer and worked in a pure java consultancy. I could have recited various GOF design patterns from memory. By pure luck I was hacking about in Erlang in my spare time.

In Erlang I was managing to piece together a pretty mind bending (for me) application for distributing depth-first search algorithms over a network. I was neck deep in learning about all sorts of hard problems and it was only afterwards that I noticed that I had barely missed the lack of Java-style object orientation. I was a changed man :) I am still a Java dev but I preach OO moderation.

Objects are great, but 90% of the time you probably wouldn't miss them if they weren't there. Now I program Go in my spare time :)

https://github.com/fmstephe/Erlang-Sat-Solver


A "reread if you haven't read it before", eh? :P

That said, I certainly agree with you--it's a great article. It gives a very good perspective on the issue, and hopefully convinces people who have only ever worked with nouns to broaden their gaze.

I don't entirely agree with everything Yegge writes--like the most recent liberal/conservative stuff--but I do agree with this article wholeheartedly. But then again, I am very clearly the sort of person to verb nouns and then to lambda :).


It is probably the most entertaining of all Yegge's articles. And I think it actually gives a really useful overview of the alternatives that are available.


This is a real classic. As someone who had only done imperative programming before reading this it really inspired me to give FP a try and led me down the road to trying Scala and then Haskell. However a year into this adventure, I still prefer "nouns". I think the human brain really does work imperatively for most logical problems. I can't deny that for a single problem the functional solution is often more " beautiful" , but the end user can't see the beauty of my voice and I can always think of an efficient imperative solution much faster. I hope one day I'll have a functional epiphany and be able to write Haskell as fast as I can write Java but so far I haven't had such luck.


I think you've mistaken Yegge's point if you see it as "Declarative functional languages are better than imperative ones." It's more about how the version of OO embodied in Java warps your program designs in an awkward way. He presents functional languages as the antithesis of this because, well, they are. But it's certainly possible for an imperative language to put functions and data on the same level — Yegge's favorite language, JavaScript, is an imperative language that does this.


I understand it was more anti-Java than pro anything else, I just saw functional as his solution to this problem. I'm already fluent enough with Java that I see absolutely no need to switch to a different imperative language as my main workhorse, so I looked at Haskell as a sort of "go big or go home" approach to weaning myself off of mutable state, etc.


I would strongly suggest you give Erlang a try. It is a fairly pure functional language, but very very simple. You don't have to user the concurrency distributed parts.

I think this is actually an important thing that is missed when functional languages are touted as the next-big-thing we are always talking about Scala and Haskell. When we go learn either of these languages you climb a steep ladder into a world of very sophisticated type systems and this becomes the focus of our discussion of functional programming.

Alas, I fear we lose the most powerful lessons that functional programming has to give us.

1: Code operating on immutable data-structures is often really easy to reason about effectively

2: Recursion is a really great way to think about programming

3: Functions are wonderful when you can pass them around like data

The type systems in Haskell and Scala, not to mention the venerable Ocaml, are amazing and powerful. But I think they are often a distraction from the core benefits that a functional code base provides.


I like first class functions. I get hung up on the trash example in this essay though. To show the commonality of verbs in a person's general understanding Yegge says 'get the garbage bag from under the sink'. WTF (Who the fuck) 'gets' the garbage? I do. 'I' is implied. I get the garbage from under the sink. Or perhaps 'Steve' gets the garbage from under the sink. These are nouns (or pronouns, whatever).

If anything, verbs do nothing without a noun to do them, and nouns need verbs to do anything. So perhaps we should just acknowledge the value of traits, objects, and functions and move on. Forgive me (but correct me first) if I have missed something massive here... I'm trying to drink as much as Steve is alleged to while writing this comment.


It is true that there is some being, or noun, that is implicitly taking out the garbage in his example. I think the important thing to note is that we can happily describe the process of taking out the garbage without mentioning who does it.

If we step back and look at it in terms of programming the principle here is that an Object Oriented insistence on defining and instantiating a doer for every action is unnecessary clutter.

When a programming paradigm makes people insist that 'everything is an object' or more alternatively 'static methods are terrible' we have strayed from simply finding ways to directly describe what we want a computer to do. We are instead forced into being object oriented.

The example nursery rhymes are a fantastic example of what this culture leads to. That is my daily life and it can be a horror.

I don't think that Steve Yegge would claim that objects are bad, but it is a very effective parody of modern Java programming (perhaps C++ and others but I don't do any of that).

I feel his essay is an excellent rebuttal to the complaint that a piece of code is 'not very object oriented' in some corners of the programming world objects have become and end in themselves. Steve is humorously pointing out that there are alternatives.


This was a very nice clarification; thank you. I think your point to look at this 'in terms of the programming principle' identifies the truth that Yegge is getting at, the functions should be first class, without the clutter of the English metaphor.


Right, OO vs functional is not either/or. It's an ever-present duality (data and algorithms) in any language. The problem is that discussions about achieving the optimum balance for a job are too subtle and non-sensational. Bloggers and commenters prefer polemics, so instead you mostly see die-hards who enjoy combative flamewars for an acclaimed tribal superiority.

Thankfully, a while back I saw an enlightening wiki page on c2.com.[1] Recognizing the yin-yang relationship where one cannot exist without the other gives a much better understanding of both.

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.

1. http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent


> If anything, verbs do nothing without a noun to do them, and nouns need verbs to do anything.

This is only true in natural language. Formal math languages are generally verb-oriented, hence functional languages are verb biased.

> So perhaps we should just acknowledge the value of traits, objects, and functions and move on.

The only thing we (OOP vs. FP fanboys) generally argue about is whether the noun should come first or the verb, which has significant implications in how dispatch is done. It really does determine whether your designs are more naturalistic (noun first) or mathematical (verb first).


> If anything, verbs do nothing without a noun to do them, and nouns need verbs to do anything.

O Canada! It's raining.


OOP goes bad when you take a centralized design and nominalize the verbs, yes. It's more fun to think in terms of trained animals: "Trash, go empty yourself outside."

    TrashBag >> emptyInto: vessel
      |here| here := self place.
      self go: vessel place.
      vessel add: self spill.
      self go: here
This seems just as reasonable as the English he wrote. (I like functional programming too.)


Trained animals is a good metaphor. Not passive objects acted upon but actors. Your example is even cleaner in Self which allows implicit 'self':

  emptyInto: vessel = (| here |
    here: place.
    go: vessel place.
    vessel add: spill.
    go: here
  )


The problem with verbs is that they are black boxes. With objects you can subclass and override methods - do stuff like the Universal Design Pattern - basing something on prototypes that you tweak here and there. Functions you can only apply, objects have parts that have names.


There is a standard technique in functional programming called 'defunctionalization'[1] which replaces functions with a data structure that can be poked or prodded or examined. It's usually implemented as a compilation technique, but can be done manually, as in Olivier Danvy's system for transforming an interpreter for a language into an abstract machine for the same language. What you are doing is, in effect, 'nominalizing' your verbs, to use Yegge's terminology. ([1] also acknowledges that it is inverse to the Church encoding of data structures, which 'verbifies' your nouns.)

[1]: http://www.kennknowles.com/blog/2008/05/24/what-is-defunctio...


That's why functional aficionados love to work with higher-order functions!


This is why the fundamental operation in the land of the verbs is composition: do this then do that. It turns out that composition is such a useful idea that we want more of it, which is where categories, monads and arrows come in.

The most obvious type of composition for functions is, well, function composition. In math we define the composition of functions f and g as f(g(x)), usually written as f ∘ g. We can write this directly in Haskell pretty trivially:

    f ∘ g = λ x → f (g x)
And, in fact, you will see this operator everywhere in Haskell, except most people are too boring for ∘ and use the ASCII . instead. (Weak.)

Also, a cool aside. I just realized that Haskell syntax is even more regular than I had assumed. In general, in Haskell, you can rewrite expressions like

    f x = λ y → ...
as

    f x y = ...
Turns out this even works for operators! So you could actually write the above composition as:

    (f ∘ g) x = f (g x)
I think that's pretty cool, but maybe I'm just easily impressed.

The idea of a category just takes the ∘ operator and generalizes it to other types, letting you use it for things that aren't normal functions. As I mentioned above, these things could be arrows or functions involving monads, but they can really be anything at all. This is, coincidentally, one of the main reasons we care about category theory: at its very heart, category theory is the study of composition. (Okay, I'm probably really misrepresenting the mathematics here, but that's how it works out for programmers :).)

There are also other things you can do with functions. In particular, you can map functions to other functions. And this is, indeed, what the well-known map function does. Normally, you think of the map function as taking a function and a list and then mapping that function over the list. In Haskell, this has the following type:

    map :: (α → β) → [α] → [β]
However, I posit that map actually does something rather different and more subtle--it takes normal functions and produces list functions. In fact, the type signature is better thought of this way:

    map :: (α → β) → ([α] → [β])
(This is why currying is so great--both interpretations are equally valid!) So, in fact, we can think of the list type as a way of mapping existing types (like a) to new types (like [a]) and mapping existing functions (like (α → β)) to new functions (like ([α] → [β])).

This operation also turns out to be exceptionally useful. So useful, in fact, that it's the basis for one of the most fundamental concepts--the functor. In fact, a functor (in Haskell) is simply any type with a function analogous to the list type's map. For historical reasons, we call this function fmap and it has the following type:

    fmap :: Functor f => (α → β) → (f α → f β)
All this says is that, given a functor type, we can map normal functions to functions over the functor. Another way of thinking about this is that a functor is roughly like a function at the type level.

Of course, we have other ways to transform functions as well; fmap is just the simplest. Similarly, we have other ways to compose functions, function composition is just the simplest.

So the most important idea is that we actually have a fairly wide variety of operations over functions. We can sling them around as easily as any other data type, really.

We move forward by combining functions rather than trying to modify them. Instead of starting with a composite piece and working inwards, we start with the basic building blocks (functions and types) and work outwards, combining them in different ways to get a composite program.

It's a difference in philosophy, and I think a rather important one.


I am happy with you with all that mathy stuff and I agree that it is cool - but it does not address my point which was that functions are black boxes. Once you have 'f' you can compose it whatever you want - but you cannot change its parts - ala universal design pattern (yet another too long Yegge rant: http://steve-yegge.blogspot.com/2008/10/universal-design-pat...).

Sure composing can somehow work for these case, just like a Turing Machine would work, after all it is all equivalent - but we really tend to think about reality in terms of prototypes - a pony is like a horse only small, a goose is like a duck only bigger and white, etc. - this is what makes this inheritance and overriding so convenient. And if you argue that it is not as clean as pure function composition, that it can quickly get into a tangled mass where pure functions are pure and easy to reason about (but it is all equivalent - so you can actually write the same tangled mess code in a pure functional language - by carrying the state around like the burlaks on Volga) - then yeah I agree - engineering is always about trade-offs. I am only trying to explain one of the sides of that trade off.


Why would you need to change its parts, it can be parameterized over the parts that change, by passing in other functions as arguments. It's also cheap conceptually and syntactically to create new ones, if an existing one doesn't fit the bill.


I don't really know - I've never extensively programmed in functional way (beside studies) - but the point about overriding is that you do it post-hoc you get a library class and you change it. The original author does not need to imagine all the ways you'd like to modify it - and yet by doing some basic structural design and putting stuff into methods he gives you the opportunity to override. Also I imagine that this level of parametrizing - that is if every function in a package is a parameter to another function in that package then it gets messy, but with subclassing/overriding you can change any method and it changes for all other methods.


Your explanations of Haskell concepts have been impressing me lately. Do you write elsewhere?


of course he does. every Haskell programmer has a blog. some have several.


I keep on meaning to start a blog, but haven't gotten around to it yet :/. I even have some half-finished articles lying around.

One of these days...


I am enjoying watching this comment grow. I hit refresh every five minutes to get the next installment :)


You can fake objects and subclassing with closures, in addition to making it trivial to implement the "one method interface" (aka "functor").

http://roboprogs.com/devel/2010.06.html (example using a subset of JavaScript, rather than a language likely to be unfamiliar to most)

TODO: edit example someday to get rid of "useless use of local variables" (in place of original formal parameters), fix where I call the outer functions "closures" instead of the inner functions.

Of course, it's easier to work with both real objects AND real functions/closures/lambdas -- I can use a hammer and a screwdriver :-)


I always thought this koan summed up this concept best: http://stackoverflow.com/a/501053/580947


> Of course, it's easier to work with both real objects AND real functions/closures/lambdas -- I can use a hammer and a screwdriver :-)

But do you hit your screwdriver with the hammer, or do you twist your hammer with the screwdriver? This can be a real problem if one programs in Scala.


Not too often, but I find myself using the screwdriver for a chisel, and the hammer for a plumb bob.

I probably need to buy more tools :-)


You can pass functions as arguments to functions, so verbs, too, can have parts.


That is interesting proposal (together with the higher order functions etc) - the problem I see with that is that normally you have many more methods and fields then you normally pass arguments to functions. Maybe with some aggregating.


And I think it's one of the problems with OOP - it's too easy to create class that have 20 methods and 10 parameters, and too hard to refactor it into smaller classes once you have it in such state. You start with simple - domain related classes that have a few methods and related state. Then real life happens and you end up with such monstrocities.

It's much less convenient to work with function that takes 30 arguments, so nobody write such functions - you just divide it into smaller parts. And it's much easier to refactor when most of the code don't need to deal with internal state, and when you can use closures.


It's funny that this is so popular, because I think it is probably the single most counterproductive thing written about software ever. Seriously.

As far as I can tell, the only point of speaking about the poor oppressed "verbs" is to screw up the thinking of the reader, making the reader feel sorry for them. It seems to have worked in some cases, but is there some other reason for it?

But what really annoys me is that the author is an illiterate. He doesn't know what nouns and verbs are. He says nouns don't do much, but of course its only nouns that do things; verbs are what nouns do. How can anyone read this without wanting to point out to the author that functions are nouns?

The only substance to his complaint is that Java seems unnecessarily verbose. Because some objects are one-trick ponies, it seems unnecessary to say "call" or "execute" or "doit", when that can be implied. But calling a function is calling a function, whether your language uses the word "call" or not. It is only because a function is a noun that it makes sense to use a function itself (rather than its output) as argument to another function or as a return value. You can use the function as an object precisely because the function itself is something different from calling (or if you prefer "executing" the function.


Is "eat" a noun too now?

What's the subject of the previous sentence?

If we insisted on only using gerunds + "do", English would be a very strange world indeed. I imagine it would sound much like Java often does: "Eating is done by me of a sandwich.". Sometimes even "An eating is done [...]" :)

PS: The author is hardly illiterate. That's plain uncharitable.


<i>What's the subject of the previous sentence?</i>

"eat". Words used as words are nouns in that context. Like the word "eat". Like the word "word". When something is eaten, someone or something is doing the eating, eat" doesn't eat itself.

I think it's fair to be a bit harsh when the entire essay is based on grade-school level misunderstanding of what nouns and verbs are.


The question was somewhat rhetorical.

The point I was trying to convey was that even though verbs can be used in a noun context, that doesn't really make them nouns.

Similarly functions and objects. There's a certain verbiness to a multiply function that a Cat object doesn't have.

For further consideration: "It rained." What's doing the raining?


The common complaint is that he's ok, but needs an editor. However, he is an awful writer. An honest editor would tell him to give up.


Part of the humor in this post, to those who find it funny, is that it is in fact so painfully long.

... in the same way that Java's OOP "purity" forces things to be too bloody long all too often.


Its a story, not a technical blog post.


Yegge is a great author. I really wish his collected rants book was available in paper and not just the Kindle edition.


I remember reading this. Highly recommended but the methodology dogma goes both ways.

My take on it is this. Object-oriented and imperative style is eating with a fork and a spoon. Functional technique gives you a knife. The people I'm most suspicious of are the ones who think I'd be more productive if one of my utensils were taken away.


Yeah, but you can always have static methods, which let you call a function without creating an object.


Sure, which you can use the following ways:

* C: function pointer

* Pascal/Delphi: procedural type

* JavaScript: function/expression as r-value (weakly typed)

* C#: delegate

* Java: er, ah, well, I guess you're screwed, as there is no way to pass that function around to other functions. Maybe in Java version N+1! (or Scala or Groovy) -- that is, a static function is even worse than a "virtual" function, since you cannot even pass an object/class/interface as a way to call that function (method).


* C: function pointer is a hack. The evidence of this is in the code. Most people don't even know it can be done, and it is rarely done in code(in some central piece no body looks at)

* Pascal: It's been a decade since I last looked at it, but I don't know if procedural type can be passed around.

* Java: Yep, Java doesn't have first class functions. Will give you that. However, in his example, Yegge was talking as if everything in Java has to be a object, and that somehow functions cannot be called without an associated object.

If you think C function pointers are a valid argument, I can provide a similar hack in java

http://pastebin.com/cSJ8ffar

You can call as many methods as you want. It is disturbing to me when people think C is some holy grail. C, at it is best, is conceptually weak. It is great for working with hardware, but for language features, it would be at the bottom of my list to look at.


Your example is an interesting case of using anon inner classes to be able to wrap and pass back static functions.

Alas, all the alternate functions are defined in one place in advance for the caller/user of "funcMap()", which makes it hard for an app to put in definitions for a library. To get out of that, you have to make an interface than funcMap()'s container implements, and add more code -- Yegge's point! (verbosity)

It's still a useful technique, just verbose.


1. No, its not an anon inner class. Anon inner class is when you implement a interface, by defining its methods inline.

2. Yes, it is bad that we have to write all that verbose code. Java's typechecker is useless where it could be most useful, by typechecking high level types. You have to write all this code everytime to make it happy.


Java has had anonymous inner classes for a very long time. They are a bit verbose, but did the job as a first-class functiona alternative very well back in the day, to the point that I actually miss them in C# that did have first class functions (b/c anonymous inner classes were useful beyond just first class functions!).

The only reason it has take Java so long to add lambdas is that anonymous inner classes did the job well enough for many years; the pressure to add them just wasn't strong enough (a victim of its own success, so to say).


I believe the Java equivalent is something like

  passableF = new NotAClosure() {
    public void execute(int i) {
      return f(i);
    }
  }
But maybe someone knows better than me. I've had thankfully little to do with Java recently.

EDIT: Which I guess defeats the purpose of "without an object", but...


Just to note, I am in no way claiming Java is superior. Java is inferior in ways I think are not the ones Yegge claims.

For me, these are the things Java sucks at.

1) There is no easy way to create lists or maps. Look at python; how easy it is to create a dictionary. In Java, you have to "write code" to put elements in a map.

2) Reflection is very weak. It is not type checked at compile time. You have to write a lot of garbage just to run the show.

3) The getters/setters are a hell. Not to write them mind you, but the amount of cruft that creeps in, that you have to filter out to get to the heart of the problem during reading.

4) The constructors: A a = new A() (wtf....)

5) piss-poor Generics.

6) File Operations are pure crap.

7) One class per file? Omg.. For a long time, I thought i sucked at OOPS. The single biggest reason it took me long to get acquainted with OOPS is because of one class per file. Any other class is a new file, a context switch in my mind. In Java, Objects are first class entities; it should be cheap(in terms of things i need to look at) to define, create, and switch between them.

Finally, I think Java sucks because of its verbosity. And a subliminal style that it supports. There are a lot of programmers in Java who think the best way of multiplying two numbers is to add one of them other number of times . Somehow, everything should be drawn out.


My "favourite" thing about the One Class Per File thing is that (certainly when I last used it) javac would refuse to compile the class file unless it was in a directory tree identical to its namespace, and you fully specified that on the command-line.

  # Worked
  javac org/eclipse/something/SomeClass.java

  # Failed
  cd org/eclipse/something/ && javac SomeClass.java


Yep, unacceptable dude! How dare you exhibit common sense? :)


For all of its warts, Groovy really makes much of this more tolerable in an environment that readily interoperates (call either way in/out) with legacy Java code.

Java file handling with dozens of classes still seems a horrific solution, like you said. I don't miss having to constantly check return codes and "errno", vs getting IOException, but I do miss the simplicity some days of "FILE *" and fopen()/popen(). (I also don't miss maintaining code that FAILS to check error codes while doing I/O)

Thanks for your replies, by the way. Thoughtful without being combative.


> For all of its warts, Groovy really makes much of this more tolerable in an environment that readily interoperates (call either way in/out) with legacy Java code.

5 yrs ago Groovy had the best interop with Java, and its promoters said so often, but things have changed a lot since then. Perhaps now you'll find another language has all the interop you need but without those warts.


He hee. I'm glad you think so.


and you can, in principle eat soup with a fork. but in practice you shouldn't.


This, I agree. It is in the spirit of "Beware of the Turing tar pit in which everything is possible but nothing of interest is easy."


Oldie but a goodie.


Too long. An idea could be stated in a few sentences.)

Java:

This is an instance of an mammal of an animal kingdom which doesn't include dolphins and whales, which has a..., placed within the instance of a class Plain of polymorphic shape which has some private attributes...

ML-family:

This is a member of a set of only mammals of animal kingdom, excluding dolphins and whales, of small size, which has a..., located on the member of a set of geometric figures....

Lisp:

The cat sat on the mat.


Well, with CLOS you really define your verb behaviour on tuples of nouns (methods) and the verbs (generic functions) are separate and you can, if you want, get as creative as you want as to how your methods get invoked by your generic functions.

[Mind you, it's been a while since I developed in CLOS, so apologies in advance if this is incorrect].


>Well, with CLOS you really define your verb behaviour on tuples of nouns (methods) and the verbs (generic functions) are separate

You can do this in any OO language as well: with multi-dispatch patterns. What makes CLOS so powerful is that you can do multi-dispatch without resorting to things like the visitor patters. Simply write what you mean with no boilerplate.


CLOS is really required in about 5% of very specialized tasks, such as simulations.


I'm not sure - "simple" CLOS is really pretty simple and arguably works in a more intuitive way than most OO environments. However, if you do want more sophistication, as with most of Lisp, taking the Red Pill of the MOP can take you as deep as you want to go....

[Damn - I really need to do some more Lisp development]


I would agree with this... basic defgeneric/defmethod/defclass work is straightforward. It's when you start with the advice partial methods and the MOP that things get... exotic.

(I read the AMOP earlier this year and I'm pretty sure I haven't recovered from that mind bender).


This metaphor can be the new "how programming language X shoots yourself in the foot." (http://www.toodarkpark.org/computers/humor/shoot-self-in-foo...)

I would love to see how C/C++/ObjC would also express a feline upon a fabric.


Lisp:

(sat(mat preposition(article 'cat ('the))))


(sat cat (on mat))




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: