> But Python also looks up methods at runtime. Does that mean Python also does message passing? Not quite.

I don't think Ruby's "message passing" is fundamentally different from Python's "method calls". Ultimately, both languages implementations are very similar: both look up methods by name in a hash table and then call them.

IMO "message passing" is just an alternative metaphor for what Ruby does when you type `object.name`. The metaphor fits Ruby but not Python, because the languages do three things differently:

- Ruby only looks for `name` in `object.class` (and its superclasses), whereas Python first looks in `object` itself

- If Ruby finds `name`, it's guaranteed to be a method whereas in Python it could be a different kind of value

- Ruby immediately calls the method once it's found, whereas Python (generally) doesn't - instead it returns a binding to be called later

This means that in Ruby, `object.name` is always calling a method defined on `object.class`, with `self` set to `object`. That can be re-interpreted as "sending a message" to `object.class`.

In Python, `object.name` is a more general value lookup - maybe the result will be callable, maybe not.

> both look up methods by name in a hash table and then call them.

Except Ruby doesn't? cue `method_missing`. If you take only trivial examples you're not going to see much difference, this starts to show when you involve more advanced situations e.g with inheritance, and then you're drilling into singleton classes.

> Ruby immediately calls the method once it's found, whereas Python (generally) doesn't - instead it returns a binding to be called later

Again incorrect, `foo.bar` in Ruby and Python are two very fundamentally different things.

Python returns a binding to a method because it's an attribute accessor; when you throw inheritance into the mix it ends up that that attribute is inherited from the parent class, bound to the instance (which really in python means pass `self` as first argument), and - schematically - adding `()` after that ends up calling that bound method. If there's no attribute that's a no method error. It's all very C-ish and make believe, barely a notch above Go structs and their functions. The closest parallel in Ruby would be `foo.method(:bar).call()`

By contrast Ruby is going to send the :bar message along the inheritance chain, and if someone can respond it's going to invoke the responder's code, and surprise surprise method_missing happens only if it has exhausted inheritance but it's itself a method-slash-message; Oh and by the way the message passing is naturally so lazy that you can actually modify the inheritance chain -in flight- and inject a parent responder right before calling `super`. The whole notion of `binding` is a very concrete construct, way more rich that simply "hey I'm passing self as first argument". It becomes even more strange to "C&al. folks" when you start to involve singleton classes and start to realise weird things like Ruby classes are merely instances of the class Class and it's all instance turtles all the way down and all stupidly simple but you gotta have to wrap your head around it.

I surmise that so many differences and surprises have with Ruby are because most languages have some ALGOL legacy and Ruby is a conceptual heir to Smalltalk (and LISP†); the whole concept of open classes being another one: nothing is ever "finished" in Ruby!

Most of the time you don't have to care about these differences, until you do.

† While code isn't quite S-expr data in Ruby, there are more than enough first-class facilities that you can create and inject code entirely dynamically without resorting to `eval`ing strings.

Nice summary. I've been using Ruby both professionally and not for going on 20 years and I just today learned about singleton_class when attempting to build something like Rails' view helpers from first principles.

Thanks. I've been trying to put these bits in a succinct format:

https://lloeki.github.io/illustrated-ruby/

Each chapter kind of builds up on the previous one. It's still WIP and far from complete but what's there has helped me onboard a few people to Ruby already.

You might be interested in the Classes and Ancestry chapters.

> If Ruby finds `name`, it's guaranteed to be a method whereas in Python it could be a different kind of value

This is the big difference, Ruby objects have no members other than methods, external entities cannot read data from the object only call methods; the object controls how it responds.

There's ways that seem to bypass this in Ruby, but they are cooperative, relying on calling other methods on the object, like accessing instance variables by calling object.instance_variable_get(:@foo).

But here you refer to implementation details. Isn't the point about message passing that objects themselves communicate with one another through such messages? And in turn, messages can also be assumed to be small objects. I don't think any of those implementations really fulfil that as a meaning. Or perhaps I misunderstood Alan Kay here. He drew inspiration more from biological cells and communication pathways therein. Naturally biology can not be mapped 1:1 onto in-silico hardware, that wouldn't even make sense either - but for instance, erlang's model appears to me closer to different objects happily communicating with one another in a very flexible, safe, fault-tolerant manner. I don't think it is the implementation detail that is about message passing. I also don't think "sending a message" is the confinement either - it's an important but, but does not appear to capture all that is meant with a "message". A message could be many things, including objects that themselves could change at any moment in time again. I see it more as an intrinsic part of communication at all times.

> This means that in Ruby, `object.name` is always calling a method defined on `object.class`, with `self` set to `object`. That can be re-interpreted as "sending a message" to `object.class`.

Ruby Instances can have their own methods and override methods separate from their class.

More precisely, Ruby instances each have a (empty initially) “singleton class” that comes before their “regular” class in method resolution order, and to which methods can be added.