Paul said:
I guess that's one of the downsides of an inside-out object
system.
Can I jump in here? You see, I have a theory.
It goes something like this. Java and most OO languages are actually
noun-oriented languages. They follow the natural way of thinking:
subject does verb to object. 1 + 1 = 2. And so on.
Lisp is verb-oriented. It goes (verb subject object1 object2) or (+ 1 1).
Right off, Lisp seems less intuitive. But that doesn't necessarily mean
anything.
However, it will also affect namespacing.
With Java, you'd have packages that then contain nouns (classes), in
which you implement verbs (methods). So suppose there's Integer, Float,
and Double and they all have plus verbs that take their own kind. I add
Complex and create a Complex.plus(Complex) verb inside it, and stick it
in my own namespace.
Good, good.
With Lisp, you'd have packages that then contain verbs, in which you
implement versions for different nouns. So you'd have a system package
containing plus, which in turn contains plus(Integer, Integer),
plus(Float, Float), and plus(Double, Double).
Now you can see where the problem will show up. If I want to add a
Complex noun, now I have to add a plus(Complex, Complex) to plus. Which
means I have to modify something in the system package. Uh-oh!
It all boils down to which programmers will be doing more often: adding
new nouns which may be used with already-existing verbs, or adding new
verbs which may be used with already-existing nouns.
Adding new nouns which are used only with new verbs is not a problem in
either case.
Adding new nouns which may be used with already-existing verbs is not a
problem if namespaces contain nouns, which contain implementations per
supported verb. But it is a problem if namespaces contain verbs, which
contain implementations per supported noun.
Adding new verbs which may be used with already-existing nouns is not a
problem if namespaces contain verbs, which contain implementations per
supported noun. But it is a problem if namespaces contain nouns, which
contain implementations per supported verb.
So, if new nouns with existing verbs is more common, Java's method is
better. If new verbs with existing nouns is more common, Lisp's method
is better. (Leaving static vs. dynamic typing entirely alone here and
considering only the order of scoping of nouns and verbs within
namespaces.) Doing the "wrong" one in either language will cause
headaches unless there's a way to namespace separately by noun and verb,
which would cause its own inherent variety of headaches instead.
My own experience tells me that new nouns with existing verbs is more
common. I quite often want to define a new Java class that has state. I
much less often wish for an existing Java class to have a method I've
never heard of before.
The workaround also isn't usually that bad: create a utility class with
a static method to do the manipulation. It can't be polymorphic or use
the object's private state directly, but the latter is for the best and
the former, while annoying, isn't usually a show-stopper.
The Lisp way around would present problems. On the rare occasion I
wanted to augment an existing class with new behavior it would be easy:
define my own verb in my own namespace and set up somehow a polymorphic
dispatch on object type. On the much more frequent occasion I wanted to
add a new noun, I'd often be forced to change verbs that lived in
pre-existing namespaces, and maybe even in more than one namespace; the
code for my new noun would be scattered all over the codebase AND the
namespaces within the system, rather than in exactly one place in each.
Ouch.
There's also going to be big differences in polymorphism. With an object
oriented system, nouns can be subtypes of other nouns, and verb behavior
can be inherited, with or without modification. Dispatch is based on the
run-time-type of the noun, and adding a new polymorphic type is easy. In
Java, suppose there was an Addable interface specifying the plus method,
and Integer, Float, and Double implemented it. If I created my Complex
class and implemented Addable, code that did generic sums over Addables
would automatically also work with Complex now. I wouldn't have to make
any changes outside the Complex class.
With Lisp, I'd need to change the system namespace's plus verb's
dispatch table manually so that it knew about my Complex class, on the
other hand. Both problems in one. In fact, every combination of noun and
verb in dynamic dispatch needs to be explicitly specified somewhere.
From the sounds of it, I couldn't just say "Complex is a type of
Number" in Lisp and have the compiler automatically know where to
dispatch plus, minus, times, and dividedBy; I'd have to edit four
separate dispatch tables in four separate places, AND define (hopefully
all in one final, fifth place) my Complex implementations of these that
the new dispatch table entries would point to. With Java I'd just have
to define the implementations, naming them correctly, and stick
"implements Number" somewhere and away I'd go.
No, he's saying that support methods that don't make up a library's API
won't be exported from packages, so there's no hazard of name clashes
between them when one chooses to import all symbols from a package.
That's the same thing isn't it? He says "private", you say "won't be
exported"; it looks the same to me.
Not boilerplate. Just resolving a couple of conflicts if they arise.
At each place where it arises.
This can happen in Java too, if the same piece of code uses two
different classes with the same unqualified name. You can end up with
code littered with "java.util.Date" and "javax.sql.Date" for instance in
some types of enterprise code. It's god-awful. All those repeated
"java.util." and "javax.sql." prefixes add up to quite a lot of boilerplate.
Fortunately, it's rare to get name clashes between different peoples'
nouns and still need to intersperse both in one bit of code. It's much
more common with verbs:
someIOStream.close();
aDoor.close();
aPort.close();
aSocket.close();
aTransaction.close();
if (difference(pointA, pointB).close()) doSomething();
I'd hate to have to fully qualify all my "close"s, or "open"s, or
"free"s, or "release"s, or "next"s, or "remove"s, or "equals"s, instead
of the correct one being inferred from the type of the expression to the
left of it.