and Objective-C to have selectors for virtuals as well, and also
languages like python to use plain strings as callables of an object.
Of course, java can be used with callable objects but that works a
bit differently. There are atleast two approaches. In one way it is
possible to use plain strings via the reflection api where the
virtual table of an object can be scanned and a method bing called.
However the amount of code is enormous to achieve such - and it
should be limited to special cases and probably wrapped in a server
instance. Examples are loadable plugins where some entries can be
looked up at runtime whether they exist or not.
The other widespread variant is with anonymous classes. That looks
like
object.addCallback (new CallbackClass {
runCallback(Object o, int param) {
another.trigger(o,param) ....; } }
which actually has a number of little problems. First of all, the
addCallback does not actually check for a matching call signature
of the embedded function. Instead the CallbackClass has an abstract
method that requires to implement the runCallback - yes it must be
under that name and no other.
The problem is with dynamic slot assignment - if you want to connect
a signal of some sort of object to trigger of another instance (as
above) you are required to write down a number of lines instead of
just doing `object.addCallback(another.trigger)`. Note
that the shorthand in the prior section did actually lie to us (and
most java docs do as well...) as it did not list how some "another"
instance comes about to call a trigger at. If the other instance is
just "this" then the following idiom would need to be used:
interface CallbackClass { void runCallback(); }
class MyClass {
final MyClass _this = this;
object.addCallback (new CallbackClass {
MyClass self = _this;
runCallback(Object o, int param) {
self.trigger(o,param) ....; } }
}
Now look again at it - it is not anymore obvious that it is
supposed to be a lambda callback. The actual intention of
calling a method "trigger" upon a signal "callback" is buried
in a lot of stuff that the programmer has to get right. That
is suboptimal especially in situations where a lot of callback
channels are attached to class. Building a VFS (virtual file
system) wrapper or an optimizing AST (abstract syntax tree)
compiler - that is getting very long for a code.
no implicit iterators
A great benefit of object oriented languages (with garbage
collection) is the available of container types that hide most
of its implementation details. Still they must be usable by
third party code on an item by item basis. There are usually
two approaches - walking the container with an iterator item
variable of the current item (and putting it as subject to some
function calls) - or giving a lambda to an "apply" routine that
calls the function on all the objects (usually filtered by some
condition). In the prior section we saw why lambda-apply is not
the usual way to do it in java.
Walking the tree is done explicitly with container functions and
it looks quite often this way:
for (Iterator iter = map.keySet().iterator() ; iter.hasNext();) {
MyObject item = (MyObject) list.next();
and note that you have do downcast the MyObject since the original
type is not expressed with the iterator. In a language like python
it would look like
for item in map.keySet():
which is really all about it since the python interpreter will
have a look at the keySet (via reflection) for an iterator()
method and use it to construct the iterator checking also whether
the returned object has the needed methods (again via reflection)
and calls them for each loop round implicitly setting the "item".
Some might have heard about Java-1.5 and its for-loop shorthand
syntax which looks like
for( item : map.keySet().values())
and which is really short and nice - but it suffers a problem
that python had as well before. The problem is in the actual
contruction of the item space! Supposed you want to run through
all numbers of 1...100000000 then one could say "range(1,10000000)"
which returns an array with one million objects being actually
allocated in main memory. Only after construction of that array
the for-loop shorthand will walk over each item in that array.
That is different to the earlier iterator-model which only constructs
one item per each step. And a language like python had seen such
problems come up in real code (with blowing main memory) and leading
to the definition of an "xrange(1,1000000)" object which does not
return an array at all but instead constructs an object implementing
the iterator model that is catched implicitly by the "for"-operator.
In other word, it would call an xrange.next() and not xrange[i++].
That's about implicit iterator detection on for-loops.
template collections
As seen in the previous section, it would be great to avoid the
dynamic downcast of the objects of a container. Each downcast
can yield a throw with an unmatched object class - which can
really happen when some third party code had been adding an
object to the container. The third party code will not see a
warning about it since an Object is an Object.
The Java-1.5 mode has been helping here with allow to specify
item restrictions for the containers. In its syntax it does
look very much like the c++ idiom for templates even that it
actually isn't. A "Map" will not specifically
create a subtype of a "Map" instead it just adds additional
arguments on "Map"-object construction. Now, on every "insert"
operation to the container the given "class-references" are
checked with the "instanceof" operator, i.e.
insert(Object k) {
if (!(k instanceof _MetaType1)) throw BadMetaType
Luckily howevever we do not need to write that down everytime
since the java compiler can do it for us - just add the
"instanceof" attribution to the declaration types of your
function arguments, i.e.
insert(Object instanceof _MetaType1 k) {
and we get the benefit that it is not only checked at runtime
on the (dynamic) objects but the compiler will also check
the class attribution of the object (static) class giving an
early indicator if something has gone wrong.
Just be sure however that this variant of type collections is
quite different than other ones. In c++ a new class is generated
and linked, they even have different static constructions and
such. In java we do not have generation and instead we have a
number of "type contraints" on an object instance being of the
general type "Map". That allows for some backward compatibility
since you can easily add a type-contrained Map to a free Map.
And of course, you can reassign the Map reference to a Map
reference with a different type-constraints but still being
the same object instance (note: assumed detail, 1.5 does just
exist as alpha code at the time of writing).
meta variable members of objects
Whoever has been using python for a compiler construction work
has come to love the idiom - all members are actually names
just listed in dictionary in the instances. Therefore, the
source code `object.member` happens to be strictly identical
to the call `object.__dict__["member"]`. There are no source
level type attributions here anyway.
The real benefit in compiler construction comes from the usual
mode of usage of an AST (abstract syntax tree) as input being
step by step transformed into another tree being subject for
output generation (e.g. gcc RTL). In each step we have to
walk the tree and look at each node and then go to reorder
some nodes and add memorize additional attributes about the
nodes and the tree somewhere.
If there are meta variables around then it is quite easy to
memorize additional attributes - just store them on the nodes
themselves. The meta variables are not needed to be predeclared
in any way. While that can be achieved with a local Map it is
about the syntax extension to access them with a shorthand
notation that just says `access attribute`.
Similar but more beyond is the wish to reorder the tree step
by step. In some cases the data structure changes so much that
the member functions must be adapted as well to give reasonable
results. In java one would have to create a new Node object of
a different class with newer methods being actually a copy of
the old Node with older methods. The copy() operation needs to
be implementated for each transition phase.
In a language like python one can simply override the member
functions on the live object. That is quite often easier and
time- and space- efficient. For those who wonder: in java the
class method table is effectivly a const object compiled into
a code segment (same in c++) but in python the method table is
copied for each object instance - into the __dict__ which
every object has anyway.
One can simulate the latter with an embedded object of each
node being the operation wrapper. Then we can exchange the
operation wrapper being often similar to the anynomous class
objects seen above with the callback mechanism explanations.
It just looks wrong since the optable object (i.e. partial
vtable) needs to store a backpointer to the parenting object
in order to achieve the intended effect on the actual object,
and the implementation of each of the worker functions can
not rely on "this" but needs to use an explicit (back)reference.
Missing Typedefs
In some article about java it was argued that it is not actually
needed. However real life shows a different matter of fact. The
problem comes in when two packages declare the same class name.
That is not so rare, a "Window" and a "Frame" are quite generic
words - just as "Page" and "Item". If there would be "typedef"
then it would be possible to declare local name from the fully
qualified name:
typedef java.awt.Frame WFrame;
typedef net.phone.Frame PFrame;
The java language does not even support such a thing as special
syntax upon "import" - most other languages allow that renaming
upon import but in java you are bound to an error message when
trying to import both names from the two packages - java does
not want to have two different symbols under the same name.
// import java.awt.Frame WFrame;
// import net.phone.Frame PFrame;
import net.phone.Frame; // can only import one of the Frame names
Note that the makers of Java/Swing were very much aware of this
problem since a java.awt.Frame would hardly be usable in the
same context as javax.swing.Frame - and therefore it is really
called javax.swing.JFrame, and that's no joke. Yes, we see a
doubled prefix. What if we run out of 26 single chars for
differentiation, or two different third party modules pick up
the same singlechar prefix for the names?
Oh btw, the same problem is not valid for constants or members
of imported packages - the reference mechanism allows us to
delcare a local reference variable and give it the actual
reference to the single object by its fully qualified name.
The local reference variable may have a truly differet name
than the object within the ouside package.
2004-01-30