There's one big difference in terms of convenience in many common implementations of classes and methods. There are essentially three different pieces involved:
In OO notation, we have A.B(C)
In almost all languages, you can store C in a variable and supply it "later":
z = C
A.B(z)
You can also store A in a variable and supply it "later":
x = A
x.B(C)
However, it is much more rare to be able to store B in a variable and supply it "later". Often, you must store A.B:
xy = A.B
xy(C)
This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!
With functions, things get easier. If you have B(A, C), you can generally just store B in a variable on its own:
y = B
y(A, C)
Some OO languages allow you to store plain methods that aren't yet bound to a particular instance (JavaScript, modern Java) but far from all do, without resorting to inconveniences like
y = lambda o: o.B
Please note that what this workaround accomplishes is effectively to turn the method into a function!
I suspect the main argument here is that it just doesn't make sense notationally to treat the first argument to a function specially. It only introduces wierd edge cases that have to be worked around. Plain old function notation has stood the test of time for good reason: it's flexible and handles most of the common cases we want.
>There's one big difference in terms of convenience in many common implementations of classes and methods.
Which ones?
>However, it is much more rare to be able to store B in a variable and supply it "later". Often, you must store A.B:
How is it rare? Smalltalk doesn't require you to specify the receiver when storing a symbol. Neither does Objective-C (selector). Neither does Ruby. Nor Java.
Which commonly used languages require one to have the receiver tied to the method when memoized? C++ is the only one I'm aware of. So it might be rare to do it in C++, but not in OOP generally.
> Even C++ lets you store the address of the member function and lets you invoke it with an instance later.
Not for virtual member functions (not that I've been able to find). C++ is geared towards generating optimal executables. Since the type of the object is known at compile-time, the particular function used for that type is known, and the compiler will generate code that applies for it.
This is why C++ uses mangled names: to discriminate between identically named functions of different types and signatures.
I like the approach Wouter van Oortmerssen uses in Lobster:
class Animal:
alive = true
class Cat : Animal
def hello(): print "meow"
class Dog : Animal
barked = 0
def hello(d::Dog):
print "bark!"
barked++
let d = Dog {}
d.hello()
let a:Animal = d
a.hello()
In other words, A.B(C) is just syntactic sugar for B(A, C), and class definitions are just syntactic on top of that. The relation between OO and regular functions and stateful objects is completely transparent (the only real gotcha I can see is that a.hello() still works here because it was assigned a subclass where this method is defined). This makes it easy to store B for later usage, like you wanted to do in your example, and reason about its behavior.
This is also the approach taken by Nim and D, and it is known as the UFCS (Uniform Function Call Syntax)[0], and I agree - it's the best of all worlds.
> This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!
That is also its advantage, since the method to call can depend on the instance. Not just for dynamic dispatch either. A statically dispatched call will look up B on classof(A).
> This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!
The time you "select which method to call" is when you write the code, right? You select it once. Like, "printf(arg);". I selected printf (in my mind), I called it. I don't need to do "x = print; x(arg)".
> With functions, things get easier. If you have B(A, C), you can generally just store B in a variable on its own:
> y = B
> y(A, C)
>Some OO languages allow you to store plain methods that aren't yet bound to a particular instance (JavaScript, modern Java) but far from all do...
Sorry, I lost the plot here. If you have B(A, C) then you don't need "y = B". B is an invariant of sorts. Just like you don't usually store the address of a class method; you just call that method on an object. Nobody writes:
> x = &objectInstance::method;
> x(args);
People just write
> "objectInstance.method(args);"
Therefore in your case, if using functions, you just do B(A, C). No assignment needed, works in every language.
In OO notation, we have A.B(C)
In almost all languages, you can store C in a variable and supply it "later":
You can also store A in a variable and supply it "later": However, it is much more rare to be able to store B in a variable and supply it "later". Often, you must store A.B: This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!With functions, things get easier. If you have B(A, C), you can generally just store B in a variable on its own:
Some OO languages allow you to store plain methods that aren't yet bound to a particular instance (JavaScript, modern Java) but far from all do, without resorting to inconveniences like Please note that what this workaround accomplishes is effectively to turn the method into a function!I suspect the main argument here is that it just doesn't make sense notationally to treat the first argument to a function specially. It only introduces wierd edge cases that have to be worked around. Plain old function notation has stood the test of time for good reason: it's flexible and handles most of the common cases we want.