Hacker News new | past | comments | ask | show | jobs | submit login
Kay on the Meaning of "Object-Oriented Programming" (fu-berlin.de)
119 points by fogus on June 7, 2011 | hide | past | favorite | 47 comments



I like the notation "<-" instead of "." Here's why: "<-" looks like sending a message. "." looks like a scoping construct.

Thus, to my brain, "." implies synchronicity: Invoke this function and continue with the result, it's just that the language implementation will do some fiddly stuff to look up the function's implementation in an inheritance hierarchy at run time.

Whereas "<-" implies asynchronicity: Send this message to this independent entity that has its own memory, processor, whatever. If you need a reply, send a continuation along with it, a'la current Javascript style.


You've almost just described E (http://erights.org). The recipient of a message sent with "." must be in the same E process (called "vat"), while with "<-" the recipient may be remote (but needn't be) and the value we immediately continue with is a promise for the eventual result. (Each vat receives one message at a time and runs it to completion before taking the next off the queue, Actors-style.)

This was the language that taught me what Kay was talking about.


I've felt for a long time that the tendency to equate "sending a message" and "calling a function/method" has caused a lot of problems - one is more like IPC/RPC and the other is something that happens within the local context (process/thread/etc.).


sending a message = piping

sending a message with callback = continuation passing style


I think the Transputer/Occam and Google Go have a better approach where they were influenced by Hoare's CSP:

http://en.wikipedia.org/wiki/Communicating_sequential_proces...


Kay mentions the Burroughs B5000. Boy you have to be an old timer to have worked on that. I did some Algol programming on the B6500, the next generation. They were stack machines, but what was interesting was that the memory words were typed in the B6500 and on. IIRC, the add instruction was not typed as it is in nearly all other machines, but it did a integer add or double add based on the tag bits of the operands. Wikipedia has a good article, http://en.wikipedia.org/wiki/Burroughs_large_systems


I think the Burroughs CANDE shell was by far the worst user environment I have every used on any system - the only nice thing (at least for Tron fans) being at least things were being fed to the MCP:

http://en.wikipedia.org/wiki/Burroughs_MCP


In those days everything was terrible. We had to deal with 360 JCL an similar abominations. You couldnt dignify them with the term shell. It was timesharing and ultimately Unix that liberated us.


What does Kay mean by late-binding? ... not the dynamic binding of overriding methods in a subclass or virtual functions (C++)? The last paragraphs compared smalltalk with lisp in this respect.


Not being in his head, I'll try to guess from what I know of smalltalk. It means the actual binding of a message to the method to execute is done only when the message is received. TOo give you an idea, you may associate some code to specific messages to an object at runtime, you may also implement doesNotUnderstand and decide how to handle messages who don't have corresponding methods (forward it to another object, have some special behavior, create the method on the fly as needed).


  Foo.counter = 0;
  Foo.prototype.handle = function(message, data) {
    if (message == 'bar' && Foo.counter < 3) {
       return this.bar(data)
    }
    else {
      throw "I forgot about 'bar'."
    }
    Foo.counter++;
  }


Not too sure about what the point is, or maybe it is just my limited knowledge of javascirpt that's playing me tricks. In any case I didn't know of a prototype.handle function and can't figure out any way for it to intervene in the usual way of calling functions on an object.

var f=new Foo()

f.bar() //Just says that f.bar is undefined and therefor not a function

//If I add

f.bar=function(something){return "nothing"}

//Calling

f.bar("ABC") //Many times always answers "nothing"


There isn't such a function built-in, though something like it (a doNotUnderstand functionality to call a default function instead of throwing an error if the requested field isn't found on an object) has been proposed as a future addition to the JavaScript standard.

IgorPartola's example code won't intervene in the usual function-call process; rather, it provides a tacked-on means of simulating message sending behavior that ought to be included in the language's syntax under Kay's definition of OO. With this convention, you'd never call bar(), or any other method, directly; you'd call handle to send a 'bar' message to the object instead. Then, if you send a message the object doesn't understand, handle() can route it properly.


Lua has this, with metatables that can set methods to handle undefined stuff. It is actually very useful and you can do a lot with it.


Based on his references to biology and cell signaling as inspirations, I think you're on the money.


Possibly the full dynamic dispatch (AKA multi-methods) that you get with systems like CLOS - where the types of all of the arguments are used to compute matching methods, not just the type of the object you are invoking the method on.

http://c2.com/cgi/wiki?SmalltalkLateBinding


I think he does mean runtime binding, as with a virtual function call. But probably more extreme - instead of a lookup table to find the static type, a message is simply sent to the receiving object. The object itself then handles it based on its type definition (which itself could be defined at runtime).


And the object can handle messages for which no function exists at all.



Binding time was thought of as compile time, link time, runtime, and even execution time. So in the time frame he was talking about nearly everyting had early binding, compile time or link time when libraries were included. With todays languages, like Java you have runtime loading which would be considered late time binding. Dynamic languages like Python have extremely late binding where the operator/operand type binding is done at the operator execution time.


C++ virtual functions are close but it would have to have a method missing construct before it would meet his full definition.


It means when you have a name for something, the association between the name and the thing should be done as late as possible. In the case of a method call, the method to be invoked for a given message name should take place at the time of the call.

I've a lot of respect for Dr. Kay, but history has shown him to be wrong on this point: strong static typing and binding things as early as possible is how robust systems are built and how errors are minimized.

However I won't doubt that the Smalltalk approach is probably right for its original intended purpose (exploratory or didactic programming).


> strong static typing and binding things as early as possible is how robust systems are built and how errors are minimized.

Huh? What about Erlang?


Erlang is very good at making systems that can sort of muddle along and keep running despite containing serious mistakes, but it's almost no help with preventing or fixing them. Every static type (even inferred) does the work of a number of unit tests, though of course not all.


I suppose you've never head of Dialyzer which gives much better coverage than most static type systems.


No I had not. If Erlang now has working type checking, that's a huge advance. But I have to wonder why the Wikipedia page still claims Erlang is dynamically typed and doesn't mention this, and the getting-started guide barely names Dialyzer and doesn't demonstrate it at all (instead it's somewhere in the middle of the FAQ). How much consensus is there that it's ready for prime time?


The fact that you can analyze the types doesn't necessarily change the fact that the language is dynamically typed (accepting that the terms are kind of wavy to begin with, and I've heard people mistakenly apply the D-word to very strongly statically typed languages with type inference).


It's still dynamically typed. The dialyzer ("discrepancy analyzer") can detect many kinds of type errors statically by doing dataflow analysis over the possible types yielded by successful execution, but it's an external tool. Like lint, not part of the core language.

For a good overview, check "Gradual Typing of Erlang Programs: A Wrangler Experience " (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.160....) and/or "Practical type inference based on success typings" (http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.62....).


> I've a lot of respect for Dr. Kay, but history has shown him to be wrong on this point: strong static typing and binding things as early as possible is how robust systems are built and how errors are minimized.

Could you expand on this? I don't see how late binding is incompatible with the robustness that static typing provides. In a traditional (C/++/#/Java) OOP setting, virtual methods and dynamic method dispatch allow for late-binding of method names to behavior based on run-time type specializations, but you still have the static compile-time guarantee that a method with the appropriate name and type signature will exist. This seems to me like it has the same robustness as an early-bound model, bounded only by the sophistication of the type system. Do you consider it qualitatively less late-bound than the message-passing paradigm, or am I missing something?


Sounds like Erlang is close to Dr Kay's notion of OOP - though my Erlang-fu is not strong enough to know if it has polymorphism.


Yes. Rather than having a table of methods in a class or prototype, each process has an event loop with a set of messages it can pattern match on.

The rough equivalent of a process changing from one class to another would be tail-calling into a different recieve { (set of pattern-matching expressions) } function. Instead of changing classes, it looks more like a state machine.


Yep.

I should note that OO means "objects having state".

You can send identical messages to different processes and they can respond with (structurally) same answers, so Erlang is polymorphic enough to be OO.

"Erlang might be the only object oriented language because the 3 tenets of object oriented programming are that it's based on message passing, that you have isolation between objects and have polymorphism": http://www.infoq.com/interviews/johnson-armstrong-oop


Erlang also has support for extremely late binding method dispatch. It's a bit tongue-in-cheek to call it a lightweight object-oriented programming language, though. I wouldn't use processes as objects in practice. My successful Erlang programs usually use fewer and more long-lived processes than I use objects in C++, Java, and Objective-C.


>My successful Erlang programs usually use fewer and more long-lived processes than I use objects in C++, Java, and Objective-C.

I think I could attribute it to lack of tools in C++, Java and Objective-C. You basically forced to use objects for almost everything (especially in Java).

In Erlang, however, you have tuples, higher-order functions and pattern matching, among with process creation and message send/receive. They are different from processes (and objects) and that's where your feeling come from.


What is wrong with data?


Nothing! The extreme opposite to Smalltalk in this respect is the APL/J/K family of languages where everything is expressed as mathematical operations on multidimensional arrays of data. For example, a K programmer wouldn't use an array of rectangles, preferring to have separate arrays of widths and heights. It's a very eye-opening programming style in its own right, and a curious programmer would do well to get acquainted with both.

Here's a nice page of "K finger exercises": http://kx.com/technical/contribs/eugene/kidioms.html . To someone who grew up on modern OO languages, it's unbelievable how much K manages to accomplish in one line of code.


There is a trial version of q (a newer k variant) at the Kx website, and a couple of us are also working on Kona, an ISC-licensed k-like language, at http://github.com/kevinlawler/kona .

The k way of doing things is indeed eye-opening. There is some documentation for Kona on its github wiki page, and Hakan has an extensive set of k resources at http://www.hakank.org/k/.


Another opposite extreme is an OOP language which has no methods. And instead each "method" is really just a data variable which is a closure, making it behave nearly identically to a real method. You could avoid needing these "methods" defined for every instance by simulating inheritance using lexical scoping from a parent class.

Although you would need to provide dynamic scope or pass the first argument as self for it to truly behave like a normal method.

The simplicity of this model is appealing to me but I do not know if any existing languages use it?


It's bad, in the sense that it leads you to write tightly coupled code that depends on it. The whole point of objects are to hide data and instead expose behavior so other code is coupled to the objects behavior rather than its internal implementation on a specific set of chosen data. This leaves you free to fiddle with the internal implementation without breaking the external API, which leads to modular loosely coupled code.


But note that this is an argument for encapsulating data and not necessarily bundling it with methods as objects. You can hide data reps with functions.


It also allows the substitution of internal data structures due to profiling / runtime usage of the data.


Which is the point of modular code, to be able to fiddle with the internals for whatever reason you like without affecting clients.


agreed, I was pointing out (badly) that it could be a runtime / compiler decision and not the programmer.


We should really link to http://www.purl.org/stefan_ram/pub/doc_kay_oop_en which is still valid and may outlast fu-berlin.de.


Nope, that's down for me, too. However, I managed to snag something on Coral:

http://userpage.fu-berlin.de.nyud.net/~ram/pub/pub_jf47ht81H...


I get a 403 from that link too...I've actually never seen that from Coral before, anyone know what's going on?


aeohu




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

Search: