custom operators

119 views
Skip to first unread message

Ryanne Dolan

unread,
Feb 6, 2010, 4:48:20 AM2/6/10
to golang-nuts
I don't want operator overloading.  I understand that operator overloading can be evil and that Go doesn't support it for a reason.  I also understand that overloading in general complicates things for both the compiler and reader.

However, I can't help but feel that a lot of Go programming is very clumsy due to the lack of custom operators.  The canonical example:

a := b.Plus (c).Times (d)

illustrates the problem pretty well.

I propose a compromise that would allow code like:

a := b + c * d

Rather than _overload_ operators, Go could leverage its unicode identifiers to allow for _additional_ operators.  Rather than define a special class of functions (the "operators"), allow an alternate syntax for method calls.

Wherein,

a := b Plus c Times d

is equivalent to the first statement above.  The methods would be defined as usual:

func (r Real) Plus (a Real) Real {
    return r + a
}

Only methods which take one parameter and return one parameter would be eligible for use in the alternate syntax.  Then, wherever an operator is expected in the syntax, the parser could look for an operator _or_ a method name, and inject the associated method call where appropriate.

The problem of "order of operations" is largely irrelevant.  Since the built-in operators (+, -, *, etc) cannot be changed, the rules of operator precedence would not change, and would only apply to them.  Any "custom operator" as used above would just use left-to-right precedence.  So:

a := b Plus (c Times d) 

or 

a := c Times d Plus b

would be appropriate to get the expected order of operations.  Perhaps this is hard to read, but it can't be harder to read than:

a := c.Times (d).Plus (b)

...especially when you use more operator-like identifiers with the help of unicode.

This alternate syntax might also be useful for creating DSLs within Go:

check := a is greater than b

check := a.is (greater).than (b)

Perhaps I am missing a huge parsing problem that would make this feature impractical.  Or maybe it just isn't a worthwhile feature.  Thoughts?

Thanks.
Ryanne

--
www.ryannedolan.info

Thaddée Tyl

unread,
Feb 6, 2010, 6:41:07 AM2/6/10
to golang-nuts
You do have an interesting idea! Indeed, this would help improve
readability a lot! And avoid verbose Java-like constructs like
BigInteger(x).multiply(BigInteger(y).mod(BigInteger.TEN)) to say x*(y
%10).

Esko Luontola

unread,
Feb 6, 2010, 7:07:55 AM2/6/10
to golang-nuts
+1

Sounds like the same as in Scala: http://www.scala-lang.org/node/118

befelemepeseveze

unread,
Feb 6, 2010, 8:46:47 AM2/6/10
to golang-nuts
On 6 ún, 10:48, Ryanne Dolan <ryannedo...@gmail.com> wrote:

> Perhaps I am missing a huge parsing problem that would make this feature
> impractical.  Or maybe it just isn't a worthwhile feature.  Thoughts?

Parsing (e.g. in gofmt) of source according to your proposal would
require to create a symbol table and perform semantic analysis of the
declared and/or imported entities even when what's needed is just a
(now) simple syntax validity check and/or capturing the nesting
structure of blocks. That's IMO (obviously) unacceptable.

-bflm

Ryanne Dolan

unread,
Feb 6, 2010, 11:59:51 AM2/6/10
to befelemepeseveze, golang-nuts

Bflm,

I don't see why. Can you explain?  The parser doesn't need a symbol table to recognize method calls now.  I don't see how the second, equally simple syntax is any different.

I'm not a parser expert so pardon my ignorance, but it seems to me that both syntaxes are LL1.

Ryanne
- from my phone -

On Feb 6, 2010 7:46 AM, "befelemepeseveze" <befeleme...@gmail.com> wrote:

On 6 ún, 10:48, Ryanne Dolan <ryannedo...@gmail.com> wrote:

> Perhaps I am missing a huge parsing p...

Ryanne Dolan

unread,
Feb 8, 2010, 12:37:54 AM2/8/10
to Esko Luontola, golang-nuts

Esko,

Good call. Scala seems to work exactly this way.

Anyone see why it wouldn't work/fit with go?

Ryanne
- from my phone -

Ryanne Dolan

unread,
Feb 8, 2010, 12:40:33 AM2/8/10
to befelemepeseveze, golang-nuts

Bflm,

As I understand, symbol tables during parsing are only necessary to resolve ambiguities in the language.  How does my proposal add ambiguities?

- from my phone -

On Feb 6, 2010 10:59 AM, "Ryanne Dolan" <ryann...@gmail.com> wrote:

Bflm,

I don't see why. Can you explain?  The parser doesn't need a symbol table to recognize method calls now.  I don't see how the second, equally simple syntax is any different.

I'm not a parser expert so pardon my ignorance, but it seems to me that both syntaxes are LL1.

Ryanne
- from my phone -


>
> On Feb 6, 2010 7:46 AM, "befelemepeseveze" <befeleme...@gmail.com> wrote:
>

> On 6 ún, 10:48, Ryanne Dolan <ryannedo...@gmail.com> wrote:
>

> Perhaps I am missing a huge parsing p...


>
> Parsing (e.g. in gofmt) of source according to your proposal would

> require to create a symbol...

befelemepeseveze

unread,
Feb 8, 2010, 2:24:49 AM2/8/10
to golang-nuts
On 8 ún, 06:40, Ryanne Dolan <ryannedo...@gmail.com> wrote:
> As I understand, symbol tables during parsing are only necessary to resolve
> ambiguities in the language.  How does my proposal add ambiguities?

It has been mentioned in a previous mail:

Let X == a P b Q c,
and Y == a > b < c.

X may be a valid expression if, for example, P == + and Q == -. X is
an invalid expression if P == > and Q == <, then X == Y. Currently Y
can be rejected by a parser without having a symbol table. In
contrary, X can be accepted/rejected iff the parser knows what kind of
operators P and Q are, thus the symbol table must be consulted for P
and Q definitions.

Similarly, P and/or Q could be declared as a non operator declaring
identifier (e.g. a variable name), rendering X invalid in another
(ambiguous) way and again this can be detected only by checking what P
and Q is in the symbol table, probably producing an error disjoint to
the former case.

-bflm

Ryanne Dolan

unread,
Feb 8, 2010, 3:27:39 AM2/8/10
to befelemepeseveze, golang-nuts
bflm,

I see your point. I wonder if the problem is quite as big as you make it tho.

Y == a > b < c

This would be the same as 

Y == a.gt(b).lt(c)

which is only invalid because gt returns a bool, and lt cannot be called on a bool.  Of course, you could have gt/lt return Bool, with 

type Bool bool

and then

Y == a gt b lt c

could be valid.  In other words, your a > b < c example isn't a strong example of ambiguity: a > b < c _could_ parse fine, just as a + b + c does.  Or the parser could be designed to disallow a > b < c as a special case, but that special case could still exist within the language I propose.

Currently Y can be rejected by a parser without having a symbol table.

... but only because the rules for gt/lt in the grammar disallow it.  Again, this could still be disallowed in the new language; as I said before, the existing operators would be handled separately, exactly as they are handled now.

 Similarly, P and/or Q could be declared as a non operator declaring identifier (e.g. a variable name), rendering X invalid in another (ambiguous) way and again this can be detected only by checking what P and Q is in the symbol table,

I think you are confusing parsing with compiling.  Certainly you will need a symbol table at some point, but that isn't the question.  For example, now

a := b + c

could be invalid if b and c are functions... but the above expression is not ambiguous and does not need a symbol table to parse: b and c _must_ be variable identifiers, just as P and Q _must_ be "operator declaring identifiers".  There is no ambiguity.

Thanks.
Ryanne

--
www.ryannedolan.info

chris dollin

unread,
Feb 8, 2010, 3:57:11 AM2/8/10
to golan...@googlegroups.com
(Opps, Ryanne and I both made post-to-individial oops!)


On 8 February 2010 08:27, Ryanne Dolan <ryann...@gmail.com> wrote:
bflm,

I see your point. I wonder if the problem is quite as big as you make it tho.

Y == a > b < c

This would be the same as 

Y == a.gt(b).lt(c)

which is only invalid because gt returns a bool, and lt cannot be called on a bool.  Of course, you could have gt/lt return Bool, with 

type Bool bool

and then

Y == a gt b lt c

could be valid.

It doesn't take much in the grammar & AST to allow

  a > b < c

to mean the mathematically natural

  a > b && b < c

It just takes a little care to ensure that b is evaluated but once (BCPL,
I'm looking at you, or at least one of your implementations).

--
Chris "allusive" Dollin

befelemepeseveze

unread,
Feb 8, 2010, 4:16:12 AM2/8/10
to golang-nuts
On 8 ún, 09:27, Ryanne Dolan <ryannedo...@gmail.com> wrote:

> I think you are confusing parsing with compiling.  

It is now possible to detect some invalid expressions (outside the
domain of compiling - highlighter, formatter, debugger evaluator, ...)
with a few regexps and optionally a CF grammar on top of them. This
property (or part of it) would be lost, the language and it's current
easy handling in some areas even outside of the compiler would become
IMO worse. Anyway, I'm, of course, not the decision maker in what
regards Go language design.

-bflm

chris dollin

unread,
Feb 8, 2010, 4:34:46 AM2/8/10
to golan...@googlegroups.com
On 8 February 2010 09:16, befelemepeseveze <befeleme...@gmail.com> wrote:
On 8 ún, 09:27, Ryanne Dolan <ryannedo...@gmail.com> wrote:

> I think you are confusing parsing with compiling.  

It is now possible to detect some invalid expressions (outside the
domain of compiling - highlighter, formatter, debugger evaluator, ...)
with a few regexps and optionally a CF grammar on top of them. This
property (or part of it) would be lost,

I don't see why the situation is any worse than it is at the moment.
The interesting property is type-correctness, which the tools you
describe can't do anyway -- or, if they can, can type-check the new
expressions just fine.

--
Chris "allusive" Dollin

chris dollin

unread,
Feb 8, 2010, 8:21:26 AM2/8/10
to golan...@googlegroups.com
On 8 February 2010 12:21, chris dollin <ehog....@googlemail.com> wrote:
On 8 February 2010 12:06, Honza <befeleme...@gmail.com> wrote:

If I'm not wrong, then you should soon see for yourself, that with
your proposal it wouldn't be anymore possible to build the AST without
the symbol table. Among other things, package go/parser would become
unusable in it's current implementation and I do expect the same
applies to essential parts of gccgo and *g compilers. They'll not be
able to rely on yacc generated parsers anymore.

[It's not my proposal.]

I think I may have sussed it.

Given the proposal, sequences of names of length 3 + 2n, eg

  A B C      A B C D E    A B C D E F G

become legal expressions, and some context-free tools will be
unable to be helpful if those happen to be type-incorrect, since
there's not enough punctuation for them to check.

Is that (all)  what you're worried about?

--
Chris "allusive" Dollin

Nigel Tao

unread,
Feb 8, 2010, 8:39:59 AM2/8/10
to golang-nuts
Personally, I don't think the small difference between these two:
a = b.Plus(c).Times(d)

a = b Plus c Times d
is worth the extra complexity.

The difference between these two:
a = b.Plus(c).Times(d)


a = b + c * d

is certainly more dramatic, but it is also even more complex to
specify and implement.

chris dollin

unread,
Feb 8, 2010, 9:05:32 AM2/8/10
to golan...@googlegroups.com
On 8 February 2010 13:39, Nigel Tao <nigel.t...@gmail.com> wrote:
Personally, I don't think the small difference between these two:
a = b.Plus(c).Times(d)
a = b Plus c Times d
is worth the extra complexity.

I agree; I'm not arguing in favour of this suggested feature, just
against arguments that seem to me to have no basis. 
 
The difference between these two:
a = b.Plus(c).Times(d)
a = b + c * d
is certainly more dramatic, but it is also even more complex to
specify and implement.

More complex, but not necessarily particularly complex.

--
Chris "allusive" Dollin

Beoran

unread,
Feb 26, 2010, 6:15:49 AM2/26/10
to golang-nuts
Interesting idea. I think thattthe problem of the symbol table doesn't
occur if you use
real operators, but let the compiler translate them to method names.
So,
a + b * c
is translated to
a.OpPlus(b.OpTimes(c))

Then you have to define those, operator methods of course. This is
much like how Lua is doing it, and they also use a very simple lexer
and parser.

Reply all
Reply to author
Forward
0 new messages