> Making calls and sometimes they get picked up and sometimes not?!?It's the basis of mainstream OOP as well. When you make a method call, you only know by loose protocol what effect it has, which objects it has an effect on or even whether it has an effect at all, as opposed to manipulating the data structure directly. It is the recipient of the message that decides what effect it has. This doesn't preclude having some mechanism to tell whether the message was accepted or not.
IMO the significant difference between something like Smalltalk-style message passing and Java-style method calling is that an unknown message in Smalltalk is a run-time "error"—which is passed as a #doesNotUnderstand message to the object that invoked the original message—while in Java it can be checked statically because the object definition specifies what messages it somehow handles.
A simple one-way message passing OO architecture in C, using a global set of messages:
#define SEND(_objp, _msgp) (((struct Object *)(_objp))->dispatch((struct Object*)(_objp), (struct Message*)(_msgp)))
enum MsgStatus list_dispatch(struct Object *self, struct Message *message)
{
switch (message->method) {
case MSG_INSERT:
insert_into_list((*struct List)self,
((*struct InsertMessage)message)->index,
((*struct InsertMessage)message)->value);
return STATUS_OK;
case MSG_DELETE:
// ...
return STATUS_OK;
default:
// Maybe the parent implements the message
return SEND(((*struct List)self)->parent, message);
// or we decide that this isn't a valid message for this object
return STATUS_NOTUNDERSTAND;
}
}
// ...
// Construct and send an INSERT message to someList
createInsertMessage(&insertMessage, 0, "hello");
status = SEND(somelist, &insertMessage);
Now, every object is encoded by a struct starting with an Object struct which contains a generic dispatch function pointer, so that each object can encode their own dispatch logic and handle whatever messages they receive as they see fit. In this case, the ListMessage struct also has a parent field, which it defers unknown method calls to. If it did not, it could just return a method-not-found status code. It doesn't have to know whether the parent implements the message.
Likewise, every message starts with a Message struct which contains the message type/method name. Depending on the type it can be cast into more specific messages.
An interesting aspect of this architecture is that it doesn't explicitly implement any kind of inheritance logic. You let the objects handle that themselves. A possible benefit of having any object accept any kind of message is that you decide whether it's an error that an object could not receive a message or not. Maybe you don't care that the object couldn't receive e.g. a NOTIFY_CHANGE message.