[generics] Generic functions calls little confusing to readers

243 views
Skip to first unread message

Alexander Morozov

unread,
Jun 16, 2020, 5:54:02 PM6/16/20
to golang-nuts
For example consider this example https://play.golang.org/p/rfccpBoFIJN
There is no generics involved, but for person reading the code there will be additional cognitive load to recognize that it's returned closure call and not generic function call.
I wonder how could we avoid this confusion (apart from using different brackets :)). Maybe force type keyword in calls as well to show that those arguments are types?

Zach Easey

unread,
Jun 16, 2020, 7:50:11 PM6/16/20
to golang-nuts
Interesting find, I would expect type parameter declarations to be limited to interfaces.

Ian Lance Taylor

unread,
Jun 16, 2020, 9:09:19 PM6/16/20
to Alexander Morozov, golang-nuts
Yes, that kind of seeming ambiguity is definitely possible. I think
it's an open question whether this kind of code will always be
confusing, or whether we can avoid it with style conventions. That
is, it's already possible to write confusing code in Go, but people
tend to naturally avoid it by choosing names that clarify the intent.

Ian

Brian Candler

unread,
Jun 17, 2020, 5:36:13 AM6/17/20
to golang-nuts
Also, type arguments to generic function calls are optional:
https://go2goplay.golang.org/p/w-q65tL5wh-

Therefore, when looking at a foo(bar)(baz), you have to know whether (bar) is a value or a type, which requires scanning the code to find the definition of 'bar' in this scope or nearest enclosing one.

If bar is a value, then foo(bar)(baz) could be a generic function call (with inferred type) *and* one which returns a lambda, at the same time.  I suspect this isn't likely to occur much in practice.

There is an existing case which doesn't seem to bother many people:

foo.Bar()

Is this calling function Bar in package foo, or method Bar on variable foo?  Go could have used different syntax for these cases, e.g. foo::Bar() for the package function call, but chose not to.

Sometimes I come across this - when looking at someone else's code, it's not always instantly obvious whether "settings" is a package or a variable.  Normally you don't need to look far though.

Harald Weidner

unread,
Jun 17, 2020, 1:48:01 PM6/17/20
to Brian Candler, golang-nuts
Hello,

> There is an existing case which doesn't seem to bother many people:
>
> foo.Bar()
>
> Is this calling function Bar in package foo, or method Bar on variable
> foo?

It could also be a call of a function within the struct value foo.

foo.Bar(baz) can have at least four meanings:

1. calling a function Bar in package foo with parameter baz
2. calling a method Bar on value foo with parameter baz
3. calling a function Bar in struct value foo with parameter baz
4. converting a variable (or constant) baz to type Bar in package foo

and with generics

5. instanciating a generic type Bar in package foo with type parameter baz

> Sometimes I come across this - when looking at someone else's code, it's
> not always instantly obvious whether "settings" is a package or a
> variable. Normally you don't need to look far though.

Those ambiguities have always been there. To read foreign code, you have
to look up the symbols from left to right. Most editors can help you with
this.

That said, I am fine with the current generics proposal.

Regards,
Harald

goo...@0xc0dedbad.com

unread,
Jun 17, 2020, 8:06:36 PM6/17/20
to golang-nuts
On Wednesday, 17 June 2020 07:54:02 UTC+10, Alexander Morozov wrote:
I wonder how could we avoid this confusion (apart from using different brackets :)).

I know the brackets discussion has been had before, and that there are some technical, and non-technical reasons why parentheses were settled on, but this really illustrates the limitations of relying on a single delimiter for everything. Even in function declarations, where it's most clear what's intended, there is a significant cognitive load placed on the  reader to determine what's happening, possibly involving backtracking to a previous set of parens. I think underestimating the impact of the syntax on reading comprehension would be a grave mistake, and I strongly believe that the proliferation of parens hurts here.

This second draft removes some other concerns that I had with the contracts design, but this syntax issue has stuck out to me as problematic since the start, and I can't shake it.

Randall O'Reilly

unread,
Jun 17, 2020, 8:24:58 PM6/17/20
to golang-nuts
Here's a proposal for a syntactic modification to the draft proposal that removes explicit type parameters from functions, and just uses generic types directly (e.g., the new interface types): https://github.com/golang/go/issues/39669

This would go a long way toward simplifying the syntax and avoiding most of the worst issues with parens, while otherwise being essentially identical to the draft proposal.

Here's a sample:

// draft proposal
func StrAndPrint(type L interface{}, T Stringer)(labels []L, vals []T) { ... }

// GT proposal
func StrAndPrint(labels []type, vals []Stringer) { ... }

you just use the generic types like any other existing types! `type` can be used for an unconstrained generic type -- otherwise you use the generic interface type for constrained types.

- Randy
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/f1699384-f487-44a4-b8d6-17950b2c4913o%40googlegroups.com.

Reply all
Reply to author
Forward
0 new messages