What mechanisms could languages use to detect certain methods and handle them specially?
For example, in Python, special methods are named differently, using double underscores:
def __add__(self, other): ...
What other ways are there?
Special things require special markers.
That's the only thing that really matters. As long as your language has some easy way to differentiate a special method from a regular one, and that marker shows up early enough in parsing to resolve potential ambiguities, that is sufficient.
But to answer the question as intended, here are a few examples:
I haven't seen this approach used anywhere, but in my language method (and attribute) names aren't limited to being strings. Essentially, any hashable and comparable object is allowed, with the syntax of
bar must be a valid expression resulting in an acceptable object).
foo.bar is the equivalent of
foo.("bar"), in particular. With this representation, special methods (and special attributes in general) can be named with dedicated key-objects, like
foo.(std::add). This has a very nice benefit of simultaneously avoiding name collisions and allowing to introduce user-defined special attributes (whereas with Python's approach you would risk conflicting with either someone else's special attributes, or a newer language version).
There's also Haskell's approach, which is to make them (mostly) not special in the first place.
I'm going to use comparison as my example, because the addition operation is a little bit quirky in Haskell, and many feel that arithmetic needs to be cleaned up.
If you want to make a type comparable, you make it a member of the class
data Foo = Foo Int
instance Ord Foo where
comparing (Foo x) (Foo y) = comparing x y
Operators such as
<= are defined in terms of
comparing, so now you can just compare two
Foo values and it all works.
This relies on several interacting language and compiler features:
comparing, to the underlying intrinsic function).
As a programmer, you would very much like a comparison on word-sized integers to compile down to one instruction. Intrinsic functions is one way to achieve this.
The second point is what makes the system ergonomic. In a typical Simula-style programming language, the only place where you can declare a type to be a member of a class is at the point where the type is declared.
A declaration like this:
class Foo extends Comparable
...means three distinct things.
Foo, and declares it to be a subclass of
Foo is a member of the class
Haskell keeps types and classes distinct, so you can declare a type to be a member of a class anywhere.
Lua's solution to this problem is Metatables which the base table references.
These Metatables are themselves tables containing all the special methods for whatever it is attached to (via a special method).
Additionally, Lua prefixes "Metamethods" with
__add, which prevents collisions when using Metatables as regular tables a well, a trick often used to implement classes.