On Nov 4, 2004, at 5:24 AM, Sam Ruby wrote:

From a Python or Ruby language perspective, infix operators are not fundamental operations associated with specific types, they are syntactic sugar for method calls.

A the moment, I'm compiling x=y**z into:

    x = y.__pow__(z)

There is nothing "reserved" about the name "__pow__". Any class can define a method by this name, and such methods can accept arguments of any type, and return objects of any type. They can be called explicitly, or via the infix syntax.

Of course--I should have realized that. I knew that's how Python handles "+", etc.--don't know why I assumed exponentiation would be different.


So scratch what I said. I should have said this:

Languages tend to take one of the following two approaches when it comes to generalizing operations on basic types (numbers, strings) into operations on object types.

1) Generalization via conversion to a basic type. As an example, some languages generalize numeric addition, "obj1 + obj2", as being syntactic sugar for something like, "obj1.floatValue() + obj2.floatValue()". (That is, you do a basic operation on non-basic types by converting them into the relevant basic types, then performing the operation on those.) This is how Perl5 handles string concatenation--you string-concatenate two objects by string-ifying them, and concatenating those strings.

2) Generalization by method call. Some languages treat "obj1 + obj2" as syntactic sugar for something like "obj1.add(obj2)". That is, you generalize in the "obvious" o.o. way. This is how Python (and C++) treats infix operators.

Different languages choose (1) v. (2), and can certainly mix-and-match (take one approach for some operations, another for others). Another way a language may mix-and-match is to do (2) if such a method is defined on the object, and fall back to (1) if it isn't.

Now from a Parrot perspective: Case (1) is already handled by Parrot--it's just an exercise in code generation by a compiler. For case (2), I think these operations correspond to method calls on objects (in the Parrot sense--the stuff in src/objects.c), not MMD or vtable operations accessed via custom ops. Here are a couple of examples why:

a) As Sam says, in Python "y**z" is just shorthand for "y.__pow__(z)"--they will compile down to exactly the same thing (required for Python to behave correctly). Since __pow__ isn't "special", we don't need anything to support it that we wouldn't need for any other arbitrary method, say "y.elbowCanOpener(z)".

b) I can define arbitrary Python classes with arbitrary implementations of __pow__, and change those implementations on-the-fly, on a per-class or per-instance basis. These aren't new PMC-classes, and I don't think that the op-plus-MMD approach gives us the ability to handle that.

Summary: Both cases (1) and (2) are syntactic sugar, and case (1) is sugar for casting/conversion, and case (2) is sugar for object-method calls.

So I don't think an op really gives us what we want. (Specifically, we could define a pow_p_p_p op, but then Python wouldn't use it for the case Sam brought up.) I'd apply the same argument to many of the other p_p_p ops that we have--they don't gives us what we need at the HLL level (though they may still be necessary for other uses).

JEff



Reply via email to