Isn’t it exactly like Smalltalk messages? It’s a synchronous call, and it’s the only way to interact with the “object” state (in this case, the effect handler).
Perhaps, but it does satisfy the definition, if you go through each point:
- ‘messaging’ — as others have pointed out,
send
is very similar if not identical to Smalltalk-style messaging - ‘local retention and protection and hiding of state-process’ — state is encapsulated in the effect handlers, and is inaccessible to the computation beyond the allowed messages
- ‘extreme late-binding’ — OK, perhaps this is slightly more of a stretch, but after all the whole point of effects is that you can run them with different handlers
Anyway, my point wasn’t really about Haskell specifically… I was mostly trying to explain that ‘OOP’ is not necessarily synonymous with how C++ does it.
I’d say effect interpreters are as late-bindings as you can get!
This is the most enlightening explanation of object oriented style that I have seen so far. All these «late binding», «dynamic dispatch» words are nothing but buzz for me — I guess because no one ever told me what «binding» and «dispatch» are to begin with.
Let us check if I understand you right. For example say we have this code (in informal language):
class A
{
void m₀ ( ) {m₁ ( );}
void m₁ ( ) {print ("A");}
}
class B extends A
{
void m₁ ( ) {print ("B");}
}
b = new B ( );
b.m₀;
— This should print B
, even though we are running code defined as part of class A
and the code printing B
did not exist when the class A
was defined.
Is this what is called «dynamic dispatch» and «late binding» in other sources?
I still cannot tell why this feature is so awesome that it warrants whole languages to be written for it. Maybe it is nice to have sometimes, but why such hype?
No, that’s not quite right. The more compelling example is
f (A a) = a.m₀;
b = new B ( );
f b; // prints "B"
Why is this feature considered so awesome? I think it’s because when GUI programming was taking off we needed a convenient way of overriding some but not all behavior of widgets provided by GUI libraries.
And to clarify what the difference is, f
is defined only with knowledge of A
yet a consumer can override its behavior by deriving a subclass B
from A
.
Another huge feature of common OOP language is that you can check if some value A a
is also of type B
, which allows you to call methods for type B
for a
. This allows calling pre-defined functionality with different data, making the program more flexible.
Hrm:
…is that really the case?
This one is called “dynamic cast”, but I’ve always considered it a violation of OOP - the whole point of OOP (or at least of dynamic dispatch) is that the caller doesn’t care what the actual type of the object is, but if you dynamic-cast it to a more specific type, then that means you do care.
Or, put differently: dynamic casting is a way of moving the dispatch from the callee and its place in the object hierarchy to the caller - you’re explicitly saying “if this fruit is an apple, then treat it as an apple”, whereas the object-oriented way would be to say “I don’t care whether this fruit is an apple, I will just treat it as a fruit, and then the fruit can decide for itself whether it is an apple and what that means”.
It is, however, considered a necessary evil, because staying true to pure OOP dispatch isn’t always the best way to model the situation at hand.