> you still need to know what messages a particular object can respond to, don't you?

Messages don't need to know what messages they can respond to as all messages have the same behavioral interface (the "makeItSo" property). Where messages expect certain informational properties (information interface) then, ya, the message would need to check if the message passed to it contains the information it requires (such as a UX layout engine that is waiting for UxControl messages).

If you have some time, here are some examples:

An example using addition:

message Add (
left 5
right 6
)

or it could be

message Add (
left Subtract ( left 5 right 6 )
right 6
)

or it could be:

message Add (
left FromFieldGet ( name "someForm" field "left" )
right FromFieldGet ( name "someForm" field "right" )
)

In all these examples, the Add message does not need to know anything about the messages provided to it in the left and right property (5 and 6 are actually messages themselves). The last Add example uses a message FromFieldGet that is able to locate information from a form (in this case named "someForm" with a field left and a field right. The form itself has not been defined in the add example but would also be defined as a message passed to some system that creates the UI/UX which would expected a message of type UxControl).

We could code the usage of our message like this:

float result = message.asFloat; // the "makeItSo" as a float primitive
int result = message.asInt; // the "makeItSo" as an integer primitive
string result = message.asString; // the "makeItSo" as a string primitive

Or let's use Add in our configuration:

message FormFieldSet (
name "someForm"
field "result"
source RunAsFloat (
part Add (
left FromFieldGet ( name "someForm" field "left" )
right FromFieldGet ( name "someForm" field "right" )
)
)
)

Let's hook that message up to a ux "button"

message Button (
action FormFieldSet (
name "someForm"
field "result"
source RunAsFloat (
part Add (
left FromFieldGet ( name "someForm" field "left" )
right FromFieldGet ( name "someForm" field "right" )
)
)
)
// properties specific to the layout of a ux element on a form
)

and so on.

Each message knows nothing about what behavior the messages composed in the properties of the message (100% decoupled) supports.

A message oriented language doesn't (can't) have constructs like for loops, switches, if/then/else, etc. in it as those aren't messages if they are integrated into the language. Instead, these are also viewed as messages (first class citizens).

A for each statement is also a message:

message ForEach (
start FromFieldGet ( name "someForm" field "start" )
stop FromFieldGet ( name "someForm" field "stop" )
action ...
)

and so on.

If you look at Obj-C, it is really message passing implemented using traditional parameterized subroutines. Smalltalk is a lot more true to it's own nature but they still use methods as an abstraction for passing information between systems (For example, they will refer to #do as method as opposed to a message).

I think if Alan Kay was forced to implement Smalltalk by focusing 100% on messages (because he was told he couldn't use parameterized subroutines) we would have had a much different world today. In programming, mental models created through abstractions is everything.