www.digitalmars.com         C & C++   DMDScript  

D - infix functions as a substitute for operator overloading?

reply "Richard Krehbiel" <rich kastle.com> writes:
My thinking: Why does operator overloading exist?

I think the primary reason is to allow an infix notation for user-defined
functions.

Common compiler construction technology (YACC, PCCTS) requires the syntax be
fixed; the set of infix tokens is fixed, their precedence is fixed, their
associativity is fixed.

But maybe we've progressed beyond that.  What if it were possible for the
user to simply declare a function as "infix?"

infix int plus(int a, int b) { return a + b; }

Now you can code:

  int x, y, z;
  z = x plus y;

Pretty readable.  Clearly "plus" is a user- (or library-) defined operation,
and I *like* making that clear to the reader.

That leaves precedence and associativity.  Right now I'd guess that it would
be okay if all infix operators would have lower precedence than all built-in
operators.  We can also simplify by giving all infix functions equal
precedence, and make the user group them as needed by parentheses, but it
might be nice to be able to specify their associativity (does "a plus b plus
c" do "b plus c" first, or "a plus b" first?).

Now, ISTR that the current alpha D compiler is a hand-coded
recursive-descent parser, and I'm pretty sure that a smart man like Walter
could in fact code up syntactic analyser that is tolerant of additions to
the infix operator table (eh, Walter?).  But it means you can't build D
compilers with YACC (um, maybe; if you leave out user control of precedence
and associativity, I think it could still be done).  Oh, and it means
feeding back info from semantic analysis into syntax, and I recall reading
that Walter has decided this is bad (the reasoning for his syntactic changes
to type-casting operations).

Well, it was a thought.

--
Richard Krehbiel, Arlington, VA, USA
rich kastle.com (work) or krehbiel3 home.com (personal)
Feb 13 2002
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Richard Krehbiel wrote:

 But it means you can't build D
 compilers with YACC (um, maybe; if you leave out user control of precedence
 and associativity, I think it could still be done).  Oh, and it means
 feeding back info from semantic analysis into syntax, and I recall reading
 that Walter has decided this is bad (the reasoning for his syntactic changes
 to type-casting operations).
Can you describe why this is impossible with YACC? As I understand it, what YACC really sees is <token> = <token> <token> <token> ; Which it would parse into blocks something like blockA: x plus y blockB: z = <blockA> blockC: <blockB> ; It's the responsibility of the compiler code supplied with the blockA rule to determine what those 3 alphanumeric tokens mean. It's not particularly different from foo bar; which the compiler must deduce (from semantics) is a variable declaration. Maybe I'm missing something again... -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 13 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C6A7779.27137005 deming-os.org...
 Richard Krehbiel wrote:

 But it means you can't build D
 compilers with YACC (um, maybe; if you leave out user control of
precedence
 and associativity, I think it could still be done).  Oh, and it means
 feeding back info from semantic analysis into syntax, and I recall
reading
 that Walter has decided this is bad (the reasoning for his syntactic
changes
 to type-casting operations).
Can you describe why this is impossible with YACC? As I understand it,
what
 YACC really sees is
     <token> = <token> <token> <token>  ;

 Which it would parse into blocks something like
     blockA:        x plus y
     blockB:        z = <blockA>
     blockC:        <blockB>   ;
 It's the responsibility of the compiler code supplied with the blockA rule
to
 determine what those 3 alphanumeric tokens mean.  It's not particularly
 different from
     foo bar;
 which the compiler must deduce (from semantics) is a variable declaration.

 Maybe I'm missing something again...
Here's a YACC (like) expression of some of the C grammar, taken from the back of K&R's "The C Programming Language:" shift_expr: add_expr | shift_expr '<<' add_expr | shift_expr '>>' add_expr; add_expr: mul_expr | add_expr '+' mul_expr | add_expr '-' mul_expr; mul_expr: cast_expr | mul_expr '*' cast_expr | mul_expr '/' cast_expr | mul_expr '%' cast_expr; Precedence is defined in the grammar, that is, the grammar (which can't be changed at run time) defines that a mul_expr has higher precedence than add_expr and shift_expr (so "a + b * c" evaluates as "a + (b * c)"). Associativity is also defined in the grammar. Associativity says that if you have "a + b - c" it's evaluated as "(a + b) - c" (um, probably; C can reorder these, and you can tell by side-effects, if it can prove that otherwise the result is indistinguishable - and if overflow is possible, then it can be). Associativity matters a great deal if the side effects matter, like with the C++ "<<" iostream operator. When you code 'cout << "result is " << i << endl;', the output must go left-to-right. What I was saying is, if we rule that user-defined infix operators always and forever have a particular precedence and associativity, then we *can* build such a compiler with YACC. Assuming that the above was the top of the "expression" definition (it's not), we'd add: user_infix_expr: shift_expr | user_infix_expr INFIX_TOKEN shift_expr; And then, we'd tell the lexer that user-defined infix function names be returned as INFIX_TOKEN rather than as IDENTIFIER_TOKEN. Now, with a recursive-descent parser written in plain C, precedence and associativity rules could also be changed at run time. But you still need to know it's an "infix operator", not an "identiier", which means feedback. (...and I'm sure that *real* compiler designers will correct any of this that's wrong...) -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 home.com (personal)
Feb 13 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Richard Krehbiel wrote:

 What I was saying is, if we rule that user-defined infix operators always
 and forever have a particular precedence and associativity, then we *can*
 build such a compiler with YACC.
Gotcha! Now I understand. Frankly, it seems like left-to-right precedence/associativity makes sense for the majority of applications. Anybody got (good) counterexamples? -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 13 2002
parent reply "OddesE" <OddesE_XYZ hotmail.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C6A891A.A5881559 deming-os.org...
 Richard Krehbiel wrote:

 What I was saying is, if we rule that user-defined infix operators
always
 and forever have a particular precedence and associativity, then we
*can*
 build such a compiler with YACC.
Gotcha! Now I understand. Frankly, it seems like left-to-right precedence/associativity makes sense
for
 the majority of applications.  Anybody got (good) counterexamples?
CInt256 a = 1, b = 2, c = 5; a = a + b * c; // Should yield 11, not 15.... -- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail
Feb 17 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"OddesE" <OddesE_XYZ hotmail.com> wrote in message
news:a4o5g5$rgm$1 digitaldaemon.com...

 CInt256 a = 1, b = 2, c = 5;

 a = a + b * c;

 // Should yield 11, not 15....
He said "user-defined", not "built-in". For user-defined, I guess we can live with fixed precedence, using braces where necessary...
Feb 17 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

 He said "user-defined", not "built-in".
 For user-defined, I guess we can live with fixed precedence,
 using braces where necessary...
Right. I would say that any overloading of standard operators (with or without the :'s that have been suggested) should have the same precedence and left-to-right orientation as the original operators. But user-defined infix operators of this type should all have left-to-right orientation with relatively low precedence. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 18 2002
parent "OddesE" <OddesE_XYZ hotmail.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C70B980.E4FD2FA1 deming-os.org...
 Pavel Minayev wrote:

 He said "user-defined", not "built-in".
 For user-defined, I guess we can live with fixed precedence,
 using braces where necessary...
Right. I would say that any overloading of standard operators (with or without the :'s that have been suggested) should have the same precedence and left-to-right orientation as the original operators. But user-defined infix operators of this type should all have left-to-right orientation with relatively low precedence. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oh I'm sorry, I understood wrong. I think user defined infix operations would be cool and I could live with fixed left to right precedence without a problem. I thought it was proposed as a substitution for overloading the standard operators, but I agree that you shouldn't mess with the precedence of + and *, or many people will be very confused and that can't be what we are aiming at with operator overloading. -- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail
Feb 18 2002
prev sibling next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Richard Krehbiel" <rich kastle.com> wrote in message
news:a4dqip$28i7$1 digitaldaemon.com...

   int x, y, z;
   z = x plus y;

 Pretty readable.  Clearly "plus" is a user- (or library-) defined
operation,
 and I *like* making that clear to the reader.
Hey, I still want to be able to overload + as is! =)
Feb 13 2002
next sibling parent Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:
 "Richard Krehbiel" <rich kastle.com> wrote in message
 news:a4dqip$28i7$1 digitaldaemon.com...
 
 
  int x, y, z;
  z = x plus y;

Pretty readable.  Clearly "plus" is a user- (or library-) defined
operation,
and I *like* making that clear to the reader.
Hey, I still want to be able to overload + as is! =)
For a second, I thought Richard's suggestion was a valuable compromise. But when you think about it, it just _looks_ like... I don't know. Baby talk. Training wheels. Writing with crayon. (No offense intended.) Once you start trying to write a big hairy expression with "plus", you're going to want to rename it "pl" then "p" (or "add", then "a") to save space on the line. At that point you've hurt readability more than if you had overloaded the plus sign. Likewise, you still can't reasonably enforce against writing a "plus" function that, say, appends to a file on the disk. So who are we kidding? Unless you're going to vastly alter the way D is specified now, Walter, there are going to be a million ways to write misleading and crappy code. Giving us overloaded operators in the style of C++[1], or not, isn't materially going to change that. Many people want the overloaded operators. I don't know what other arguments I can make. Okay, okay, here's one more. Bignums. If I want to create a class that manages arbitrary- precision, arbitrary-magnitude numbers, I want to manipulate them using the conventional mathematical operators. :+: and other weirdness isn't going to help readability. -Russell B [1] And by that I mean without substantially restricting the operator list from C++ -- sure, get rid of . and -> and , but leave the bitwise, logical, and comparison operators in.
Feb 13 2002
prev sibling parent "OddesE" <OddesE_XYZ hotmail.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a4duo0$2ao6$1 digitaldaemon.com...
 "Richard Krehbiel" <rich kastle.com> wrote in message
 news:a4dqip$28i7$1 digitaldaemon.com...

   int x, y, z;
   z = x plus y;

 Pretty readable.  Clearly "plus" is a user- (or library-) defined
operation,
 and I *like* making that clear to the reader.
Hey, I still want to be able to overload + as is! =)
Me too! -- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail
Feb 17 2002
prev sibling parent Roland <rv ronetech.com> writes:
Richard Krehbiel a écrit :

 My thinking: Why does operator overloading exist?

 I think the primary reason is to allow an infix notation for user-defined
 functions.

 Common compiler construction technology (YACC, PCCTS) requires the syntax be
 fixed; the set of infix tokens is fixed, their precedence is fixed, their
 associativity is fixed.

 But maybe we've progressed beyond that.  What if it were possible for the
 user to simply declare a function as "infix?"

 infix int plus(int a, int b) { return a + b; }

 Now you can code:

   int x, y, z;
   z = x plus y;

 Pretty readable.  Clearly "plus" is a user- (or library-) defined operation,
 and I *like* making that clear to the reader.
I already suggested something like this (see "Operator overloading, ann other idea"). I was not convinced by counter arguments. So i still support infix notation Roland
Feb 13 2002