Re: Operator Overloading
David Hakim <dhakim-Gkm/TONP9n1Wk0Htik3J/[email protected]> Tue, 15 Apr 2003 22:49:40 -0400
| Newsgroups | gmane.comp.lang.moto.devel |
|---|---|
| Message-ID | <[email protected]> |
On Tuesday, April 15, 2003, at 06:21 AM, Stefano Corsi wrote:
>
>>> in reality, I've implemented only += and -=, so a working example
>>> could be:
>>>
>>> ${
>>> use "codex.util";
>>> IntSet is = new IntSet();
>>> is+=1;
>>> print is.size() + "\n";
>>> }$
>>
>> Interesting, I saw the .i file for Inset. I wonder though if what
>> should be returned from the result of += is a brand new (cloned)
>> Intset.
>
> It seems strange to me that ... adding something to something could
> give a new
> something (or maybe not: is it a metaphore of life?).
> Maybe I'm wrong but I've always imagined overl. ops as a more
> intuitive way to
> specify methods like _add, _delete, _get, and so on. And if I define
> an _add
> method for an object I'd use it to add a value to THIS object, not
> some new
> (cloned) object.
We should give serious thought to these semantics ... the driving
intuition behind += creating new objects is to maintain an equivalence
between
A = A + B
which really shouldn't alter the internal state of A or B, and
A += B
For all classes A B distributed with moto.
However, as already described, because of the cost of cloning container
classes, the += operator will likely rarely be used in practice except
for union or list join operations.
What are the semantics for the += operator in other languages that
allow operator overloading ?
> [ See below ]
>
>>> if (v1->type->kind != INT32_TYPE && v1->type->kind != INT64_TYPE) {
>>> moto_illegalTypeForArrayIndex(v1->type->name);
>>> }
>>>
>>> this would throw an error in case of [] operators used with string
>>> indexes
>>> (ex. SymbolTables: foo["bar"]). What kind of strategy should we use
>>> here? Get
>>> rid of the check? :)
>>
>> The [] operator is a tough case ... probably the toughest. We may need
>> to factor motov_array_index function back into the motov_array_rval
>> code. In general what we want to do is, prior to the default type
>> check, see if there a function / method for the specified operator
>> that
>> matches the operands past by calling motox_lookupMethodOrFn and seeing
>> if it returns anything
>
> I agree: we should grab the ops, check if the overl. op exists and
> eventually
> execute the function.
>
>>> 2) [MEDIUM] I find it redundant to write code for every operator.
>>> But,
>>> on the
>>> other side, I haven't found a good and elegant way to create a
>>> unified
>>> function, something like
>>> see_if_you_find_and_operator_for_this_unioncell_execute_and_create_th
>>> e_
>>> result(),
>>> because every operator has its own semantic meaning and its own
>>> behaviour.
>>
>> What I did in motoi (so far) is factor the code you wrote out of
>> motoi_assign into a function
>>
>> static int
>> motoi_try_overloaded_binary_op(int op,MotoVal* v1, MotoVal* v2)
>>
>> I figure we could have a motoi_try_overloaded_unary_op also or just
>> switch to a unary check if the second op is null (or based on the op
>> passed in
>> I made it return the ftable code for now ... we could change that ...
>> so the refactored motoi_assign looks like:
>>
>> ...
>> v1 = opstack_pop(env);
>> v2 = opstack_pop(env);
>>
>> if(motoi_try_overloaded_binary_op(op,v1,v2) != MOTO_OK) {
>> motoi_domath(v1, v2, op);
>> moto_freeVal(env,v1);
>> moto_freeVal(env,v2);
>> }
>> ...
>>
>
> Yes, this is good! We have probably to make some acrobatics in
> motoX_array_rval and similar, where first one value is popped from the
> stack
> and THEN, after many operations, the other values (array indexes) are
> popped
> from the stack. But we can't wait the end of the function, when all
> values
> have been popped, to call motoi_try_overloaded_unary_op...
>
What we should attempt to do is refactor motoX_array_rval/lval so that
each subscript gets a separate operation UnionCell in moto.y
This may not be all that hard since:
X[1][3][7]
is equivalent to
((X[1])[3])[7]
Both forms parse fine yet the second form only only calls
motoX_array_rval/lval on one subscript at a time. In fact I believe the
only reason there was code to evaluate the whole list of subscripts at
once was for array instantiation where all known dimensions of the new
array were needed at once.
>>> 3) [HIGH] What if someone defines an operator with an uncorrect
>>> function. I
>>> don't know where is the right place to catch this and signal it to
>>> the
>>> user.
>>> For example, imagine this:
>>>
>>> boolean IntSet::-=(int i, int i2, int i3, int i4) =>
>>> int iset_remove(IntSet *this, int i, int i2, int i3, int i4);
>>>
>>> here the -= operator expects exact one parameter, but we give four to
>>> it.
>>
>> Well ... the right place is definitely in mxc somewhere ... but I'm
>> not
>> sure offhand where either :) I'll look into this
>
> At the moment I identify an operator in mx.y with:
>
> | m_operator_declaration MAP c_function_declaration SEMICOLON
> {
> $$ = op(MAP, 2, $1, $3);
> }
> ;
>
> we could modify the c_parameter_type_list nonterminal for operators so
> that we
> limit the number of arguments taken.
>
We could ... I wonder if the change wouldn't be easier to make in mxc.c
though. Changing C code is generally preferable to adding productions
to yacc files.
> C++ checks number of arguments for the operators at compile time:
>
> char * operator<(int a,int b) { return 0; };
>
> pippo.cc:2: `pippo::operator< (int, int)' must take exactly one
> argument
> but as you can see does not check return type, and let an < operator
> return
> char *, that is probably meaningless.
>
>>> 4) [HIGH] For "method" overloaded operators (like for example +=), or
>>> in other
>>> words operators that take the object itself as parameter, there is a
>>> contrast
>>> between what the "normal" operator (+=) expects on the stack and what
>>> is left
>>> from the "overloaded" operator.
>>
>> Is there ? should there be :) ?
>>
>>> For example:
>>>
>>> int a;
>>> a += 2 means: take the value of a, add 2 to it and put the result in
>>> a.
>>>
>>> but
>>>
>>> Intset is = new IntSet();
>>> is+=2 means: perform the _add function on object "is", ignore the
>>> void
>>> result.
>>
>> With the C++ STL I believe the collection itself is cloned and the
>> clone is specifically returned by the overloaded operator. While this
>> isn't efficient I do believe it is the more correct (safer) way to do
>> things (for example we do not re-alloc strings when we use += to add
>> them). Thus in C++ at least the += operator for IntSet does return
>> something.
>
> I understand now. So, the coherent approach is to create a new value
> (the same
> operation we do for ints, strings, etc...).
>
>> I do not believe the overloaded function or method in the above case
>> should return void. If it is defined as such in the .i file, and the
>> void value is used in moto code, a verifier error should be thrown
>> saying 'void value not ignored as it should be' . We should not
>> attempt
>> to read extension authors minds :) If they say an operator should
>> return void we should let it although we should push a void typed
>> expression onto the stack.
>
> Ok. We could check number of parameters at the grammar level in mx.y
> and then
> check type and existence of the return type in motov.c. And use
> whatever
> value he states for the return type. But what about < and > and == and
> !=.
> Shouldn't we force the programmer to return "bool"?
Hmm, again I think I want to defer to the extension author here. If
he/she wants to return something different for his/her crazy class I
suppose we should allow it ... that doesn't mean we have to distribute
the extension with moto :) or provide any support for it. Of course for
extensions we write we will surely always return booleans for the
boolean operators :)
>>> 5) [HIGH] What do we do with nested operators? For example, suppose
>>> we
>>> have an
>>> [] operator in SymbolTable.i for _get operation. We can write:
>>>
>>> print <String> foo["bar"];
>>>
>>> But what if foo["bar"] is a SymbolTable? Should we be able to write:
>>>
>>> print <String> foo["bar"]["baz"];
>>
>> Of course :) Unfortunately we may have to write
>>
>> print <String>(<SymbolTable>(foo["bar"]))["baz"];
>>
>> since the return type of the overloaded [] operator will be Object.
>
> Is there any way to get rid of the casts? Or are they meant to exist?
I'm not sure if there is a way or not. Once we have inheritance the
outer cast will go away because the Object base class will implement a
toString() method (and print will always attempt to call toString() on
any object). That's what Java does at least ...
I don't believe we can do the same with operators for two reasons:
1) If a specific class does not implement the subscript operation and a
moto programmer attempts to use a subscript on an instance of that
class we want the 'Operator not implemented' error to show up at
verification time. If all operators were inherited methods from methods
in the base object class than all errors like that would be deferred
until runtime ... which is exactly the reason why I don't code in PHP
or Coldfusion anymore :P
2) Operators don't usually have fixed signatures. For instance the
subscript operator is unary but it can be defined to take ints,
Strings, or even Arrays as its argument if overloaded. Without a fixed
signature, treating all operators as 'inherited from the base Object
class' becomes much tougher.
>
>>> 6) [LOW] I have a doubt: why in motov.c motov_array_lval just calls
>>> motov_array_rval, while in motoi.c it happens the opposite? Could you
>>> explain the relationship between the two functions?
>>
>> In motov it could be either way, the logic in both is the same, for
>> either the LValue or the RValue all we care about is the dereferenced
>> variable's type
>>
>> In motoi it is easier to make moto_array_rval depend on
>> moto_array_lval
>> because moto_array_lval pushes an address onto the stack. From an
>> address you can always get a value for that address. But given a value
>> you cannot find out where in memory that value is stored which we
>> would
>> need to do for LValues.
>>
>> So probably motov should be changed to look more like motoi :)
>
> Yes, it 's always better if the two like each other.
>
> I will checkout your new branch!
Excellent! Great job so far on this by the way!
-Dave
> Stefano
>