Mailing List webobjects-dev@wocommunity.org Message #243
From: ocs@ocs.cz <webobjects-dev@wocommunity.org>
Subject: Re: [WO-DEV] ERXGenericRecord takeValueForKey
Date: Sat, 28 Aug 2021 20:38:36 +0200
To: WebObjects & WOnder Development <webobjects-dev@wocommunity.org>
Markus,

well thanks for an answer, but I'm afraid it's mostly quite beside the point. Anyway, the one single thing which seems relevant to my question is

Let’s say your EO has a “name” attribute which you define in the EOModel. Any EO can be messaged and if it “understands” the message it will act. If an object sends the “name” message to an EO it will get the contents of the name attribute back if the receiving EO understands this message. It does when it has a “name” attribute, it does not otherwise. The same is true if an object sends the “setName” message.

where the “... if the receiving EO understands this message. It does when it has a “name” attribute, it does not otherwise ...” of yours is essentially what I meant by

Looks like there's some trick in NSKeyValueCoding.DefaultImplementation.takeValueForKey which recognises modelled attributes in EOGenericRecord

but alas it does not answer the subsequent questions:

Is this documented somewhere? Far as I can say, neither ERXGenericRecord nor EOGenericRecord nor NSKeyValueCoding.DefaultImplementation mentions anything special; NSKeyValueCoding.DefaultImplementation.takeValueForKey should simply check for the accessor (either with or without the _ prefix) and if there is none (which there indeed is not), fall to handleTakeValueForUnboundKey. What am I missing here?

Let me please to re-phrase the problem quoting more of the documentation than originally (for I've originally thought it should not be necessary).

(i) NSKeyValueCoding.DefaultImplementation.takeValueForKey is documented very explicitly to
- try a plain setter (e.g., setName for a key “name”)
- if it fails, to try an underscored setter (e.g., _setName)
- if it fails, to try some fields (e.g., name, isName and more), too, unless canAccessFieldsDirectly is false. In my case it is false (and besides there are no such fields anyway, I've actually checked)
- if none of the above steps helps, to invoke handleTakeValueForUnboundKey (if implemented, which, in my case, it is).

Note that there is no mention of enterprise objects, models etc.; that makes a perfect sense, for this is the Foundation level which does not (at least, should not) know anything of them; EOF is implemented above Foundation and the lower tiers do not (at least, should not) know anything of the upper ones,

(ii) therefore, given the implementation marked [1] below, I would assume, when takeValueForKey(value,'name') is called, the following happens:
- my overridden method gets called and prints out “???takeValueForKey name”. Check, that happens all right;
- since there is neither a setName nor _setName method[2], now I would assume my handleTakeValueForUnboundKey method gets called and and prints out “???handleTakeValueForUnboundKey name”

Oops. The latter never happens if “name” is a modelled attribute (which makes it self-evident the problem is EOF-level). It happens all right for other keys (for which there's no setter method).

My question is: how on earth is is possible that NSKeyValueCoding.DefaultImplementation.takeValueForKey of Foundation, called over an ERXGenericRecord, can recognise a modelled attribute, and instead of invoking handleTakeValueForUnboundKey as documented sets that attribute's value? What the H. happens in there (and where is it documented)?

I could quite understand if EOGenericRecord.takeValueForKey was overridden to recognise and set the modelled attributes; that would make a perfect sense. Nevertheless (a) this is nowhere documented far as I was able to find, and (b) even if it was, it should apply only if I called “super.takeValueForKey” at [3], but never with the actual “NSKeyValueCoding.DefaultImplementation.takeValueForKey” code.

Far as I can say, Wonder does not override NSKeyValueCoding.DefaultImplementation, (like it does with other Foundation classes, notably the containers, e.g., NSArray) so that's not an answer either.

(And to make it even worse, not even takeStoredValueForKey gets called in the process. What the?!?)

That's my problem and that's the question I am seeking an answer for.

[2] I've checked (to be extra sure, I've checked also for the fields, regardless canAccessFieldsDirectly). The setters indeed do not exist (they hardly could, unless ERXGenericRecord or EOGenericRecord created them runtime through some very arcane Java magic; I think it is not possible, but I might be wrong. In ObjC there is an API to do such things, but Java – very unfortunately – does not support this, far as I know).

That aside, the rest is rather a completely different debate which, I am afraid, does not belong to this particular mail thread (although in different contexts could be very interesting and, at the very least for most people with a Java background, also rather at the enlightening side. I am adding a couple of comments just for possible benefit of those, if anyone such happens to read on — which I somewhat doubt, but who knows :))

On 28. 8. 2021, at 17:02, Markus Ruggiero (rucotec) <webobjects-dev@wocommunity.org> wrote:
On 26 Aug 2021, at 03:50, ocs@ocs.cz <webobjects-dev@wocommunity.org> wrote:
Your (and most other folks’s) thinking of key value coding is wrong and way too technical.

Um, “way too technical” is precisely what happens when a computer runs a compiled code. It's often expressed as the old adage “Bloody thing does what I told it to; not what I wanted to.”

Conceptually in object oriented programming objects communicate with each other. This is a 2-fold process. One object sends a message to another object and the receiving object then acts on that message.

Which applies perfectly with ObjC or Smalltalk or Ruby or any other object-oriented language; Java though is an ugly half-OO mess in which it is not that easy.

In pure Java you do not and cannot “send a message”; the thing allows you just to “call a method“ (at best found through the reflexion — which nevertheless is considerably slower than real message-sending —, at worst hard-compiled, which is about of same speed, but terribly inflexible). It's considerably closer to the C++ disaster than to a real OO language.

This distinction is beautifully visible in Objective-C where there are two syntaxes available. One, the classic C call, relies on early binding. This is just a simple function call and the called code is determined at compile time. The other syntax, the one most programmers don’t like (because they don’t understand it), is the [myObj myMessage] thing with the square brackets. This is really message passing in the true sense of the OO paradigm. In this sense you can send any message to any object, even to a non-existing object, and you either get a reaction or you don’t because the receiving object understands your message or is does not.

Indeed. Completely irrelevant for our case though. (And in ObjC somewhat misleading, for in there are other syntaxes available as well, e.g., you can call objcSend directly, which boils down to same result as [foo bar]; you can cache the IMP and call it manually, and more.)

Java is, alas, much more limited. Its (instance-level) method-calling works in a roughly similar way ObjC message-sending does, and mostly — mostly! — something like Java's “anArray.arrayByAddingObject(foo)” and ObjC “[anArray arrayByAddingObject:foo]” are functionally equivalent. Not always though; in ObjC anArray can be a proxy which processes any message received in a special way (e.g., sending it over an IPC to another process). In Java, this is very difficult — actually impossible in full extent —, leading to an incredible amount of ugly boilerplate code. (About the closest to proxying we can find in Java is EOFaulting — compare it with the ObjC implementation of faults!)

Key value coding in WO is a technical implementation of this mechanism.

Not at all.

KVC is a mechanism which allows one to access object properties by name. It originated in the very ObjC (unless it's older and Smalltalk-based; Ito be frank, I can't recall — I did not use Smalltalk in ages. Anyway, it is well possible), and has been very handy in there, regardless ObjC, being a real OO language, offers full-fledged proper message-sending naturally.

KVC can be (and was) implemented in there, as ObjC does with its “[foo valueForKey:bar]”. It can be implemented just as well over (much less flexible and much less convenient) method-calling, as Java does with its “foo.valueForKey(bar)”. In both cases, the implementation is conceptually similar (aside of the Java-induced boilerplate, but that's again beside the point).

In “modern” languages like Java one cannot send an arbitrary message to any object - there is simply no syntax for it.

Indeed, which makes Java a terribly inferior language :( It's real bad Apple thrown out ObjC-based WO+EOF. (Incidentally there are modern languages which do support proper message sending — albeit often hidden under a very misleading and ugly API which looks like method-calling; an example of these is Ruby.)

KVC is the underlying technical implementation to provide exactly this.

Definitely not. The technical implementation of something-as-close-to-proper-message-sending-as-possible in Java is the NSSelector API.

(Side note: only when Java got introspection in V1.2 or was it 1.4? it became possible to port WO to Java).

That's right, but still, even today's Java is terribly deficient; its introspection and runtime APIs are sorely lacking. As aforementioned, you can't really implement a full-fledged proxy in Java. Another problem is that you can't manage methods of an objects; there's nothing like the ObjC runtime API and it's a big bad one (e.g., you can't add accessors on-demand, as e.g., CoreData does). The Java static “methods” are not methods at all, and the way they are inherited is a bad joke (it's something like the terrible C++ non-virtual inheritance). There's more problems (e.g., there's nothing like the +load ObjC method in Java).

Frankly I can't think of worse idea of late than was the turning WO+EOF to pure Java – well perhaps but for the one to completely stop supporting WO+EOF publicly and making it a completely internal Apple-only stuff :/

Let’s say your EO has a “name” attribute which you define in the EOModel. Any EO can be messaged and if it “understands” the message it will act. If an object sends the “name” message to an EO it will get the contents of the name attribute back if the receiving EO understands this message.

Regardless all the Java deficiencies mentioned above, at this particular level, there's no difference at all. Namely, the above can be re-phrased this way for the Java-crippled API, and the result is just as convenient for the programmer with precisely same level of flexibility:

Let’s say your EO has a “name” attribute which you define in the EOModel. Any EO's valueForKey message can be called and if it “understands” the method argument it will act. If an object calls the method with the “name” argument to an EO it will get the contents of the name attribute back if the receiving EO understands this key.

It does when it has a “name” attribute, it does not otherwise. The same is true if an object sends the “setName” message.

Indeed. I am asking “why directly st the valueForKey level” (instead of handleTakeValueForUnboundKey), and “where is this documented”.

KVC implements different strategies to make this message passing possible. It looks for direct access possibilities to attributes, it looks for accessor methods and it plays with names for such attributes and methods (name, _name, name(), getName(), etc). It tries hard to overcome the limitations of the programming language to allow the freedom of sending arbitrary messages to any object.

Definitely not. What it does (and for a price of heaps of boilerplate does pretty well) is to try hard to overcome the limitations of the darned Java thing to allow the freedom of accessing arbitrary properties of any object (by name).

On the other hand, it does nothing at all to support arbitrary messages. NSSelector does that (far as reasonably possible).

This is also nicely visible for bindings in the wod or when specifying key paths in code.

Not surprisingly, since wo-bindings (incidentally, does anyone really use wods these days, instead of inline bindings in the html template?) are simply and straightforwardly implemented by KVC :)

In Java myObject.purchase().car().brand() will crash with a NullPointerException when your purchase does not include a car because you cannot call the brand-method on a null car object. The same in KVC and bindings works: value = myObject.purchase.car.brand;

Indeed, KVC does propagate nulls automatically (as any decent API should, but triple alas, Java does not).

Again current programming languages do not support something like this and depending on your code having accessor methods you might get NPEs nevertheless because you make it so hard for KVC.

Some of them can support null-propagation with specific syntax, usually something like foo?.bar. Some of them can be improved to ensure automatic null-propagation— myself, I use Groovy with my own compiler-plugins which do precisely that :) No NPE-hell anymore :) 

Man do I love this stuff! It’s the purest implementation of OO concepts I know of (other than probably Smalltalk).

Smalltalk aside, aforementioned ObjC is worlds better OO than anything Java-based. Sometimes, it gets a bit complex due to the plain-C support and plain-C types (they make e.g., message forwarding considerably more complex than it would be otherwise), but that's about the only drawback of the thing (well, perhaps along with some irregularities in the dot-syntax for accessors, and its overall syntax for closures).

This is the main reason why I use WO when teaching OO programming. I would have used Smalltalk but it had to be a bit more modern language (heh heh heh - the school said so).

Myself, if Smalltalk was forbidden and for any reason I could not use ObjC either, my first option would be Ruby. If interfacing with Java was needed e.g., to use existing libraries or so, then Groovy.

All the best,
OC

Subscribe (FEED) Subscribe (DIGEST) Subscribe (INDEX) Unsubscribe Mail to Listmaster