Please do not add aliases to the language

6017 views
Skip to first unread message

Dave Cheney

unread,
Oct 27, 2016, 10:25:25 PM10/27/16
to golang-dev
Dear all,

I'm writing to formally request that the process of adding aliases to
the language be rolled back.

Aliases were proposed via the proposal process [1], and accepted over
the strenuous objections of many external contributors, which, like
myself, have invested years of effort in advocating Go for its
simplicity and focus on readability.

It was suggested that aliases be placed behind an environment
variable, similar to the GO15VENDOREXPERIMENT, allowing Google to test
this feature internally before committing to it. This did not happen.

I was told aliases would be restricted to types as they were the only
declaration that did not have an existing analog. This did not happen.

I was told that aliases were necessary for a protobuffers feature, and
as a realist I understand that Go is sponsored by Google and that
sometimes this arrangement calls for quid pro quo. But then, this
morning I see this [2]

https://go-review.googlesource.com/#/c/32145/1/draw/go1_8.go

It is clear to me that Go programmers, including the Go team
themselves, will be unable to stop themselves from abusing this
feature to the detriment of the readability and comprehension of all
Go users.

Please, do not add aliases to the language, you're breaking my heart.

Dave

1. https://github.com/golang/go/issues/16339
2. https://go-review.googlesource.com/#/c/32145/1/draw/go1_8.go
n. https://go-review.googlesource.com/#/c/32145/1/draw/go1_8.go

Robert Griesemer

unread,
Oct 28, 2016, 12:19:20 AM10/28/16
to Dave Cheney, golang-dev
Some clarifying background information: I suggested to Dave (in-person communication) that perhaps we should introduce aliases guarded by an environment variable, not having thought enough about the consequences of such a flag when I brought it up.

The problem with an environment variable is that it effectively prevents aliases from being tried seriously, out of fear that support for the feature might disappear again. On the other hand, if they are widely (and appropriately) used, an environment variable cannot be changed anyway without breaking code. We have seen exactly this effect with GO15VENDOREXPERIMENT. Only once we enabled vendoring for good, people started using it.

Hence the decision was made by the Go team to go ahead with the minimal, regular, and quite restricted alias feature we have now implemented.

Let me say this regarding the fear of misuse: The Go language also implements the goto statement, and it is undisputed that uncontrolled use of gotos is hugely detrimental for readability and comprehension of code. Yet, there is hardly code that abuses gotos these days. But there are rare cases where a goto is exactly the right choice: when you need one (*), you really do.

I believe the same is true for alias declarations: They are seldom needed, but sometimes they are exactly the right mechanism. (**)

Thanks,
- gri

(*) Machine-generated code is a typical example.
(**) Several other important use cases besides protocol buffers were discussed in the proposal.


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brendan Tracey

unread,
Oct 28, 2016, 2:08:50 AM10/28/16
to Robert Griesemer, Dave Cheney, golang-dev
The problem with an environment variable is that it effectively prevents aliases from being tried seriously, out of fear that support for the feature might disappear again. On the other hand, if they are widely (and appropriately) used, an environment variable cannot be changed anyway without breaking code. We have seen exactly this effect with GO15VENDOREXPERIMENT. Only once we enabled vendoring for good, people started using it.

This change has much larger ramifications, and there is more significant objections to this proposal than the others. I haven’t been following code-reviews this cycle, but it seems that full alias support was added today, and the freeze is Nov. 1. That gives 5 days of experimentation?

Hence the decision was made by the Go team to go ahead with the minimal, regular, and quite restricted alias feature we have now implemented.

It doesn’t seem like the implementation is minimal relative to the discussion in the document. There was a suggestion that aliases would only work for variables, and there was a suggestion that one cannot make an alias to an identifier that it itself is an alias.


Let me say this regarding the fear of misuse: The Go language also implements the goto statement, and it is undisputed that uncontrolled use of gotos is hugely detrimental for readability and comprehension of code. Yet, there is hardly code that abuses gotos these days. But there are rare cases where a goto is exactly the right choice: when you need one (*), you really do.

The PR Dave linked and the example in the spec are what worry me about aliases. Go’s best feature is uniformity and legibility, and there have been design choices in Go that add a few keystrokes for the coder in order to make it easier on the reader. Aliases are so tempting for the coder to sacrifice clarity to save a few keystrokes. Right now, in Go it is so easy to find where the real code actually lies. Alias’s seem to beg for many levels of “harmless” indirection before getting to the real implementation. The PR Dave linked seems to be one such example.

When I’m reading code, if I see “Pi”, is it because the coder used “const Pi => math.Pi”, or because there is a different value defined? If I see “Exp(x)”, is it because the coder used “func Exp => math.Exp”, or because there is a custom implementation to work around #14932? I agree that this is a problem that can be encountered in Go today. Aliases, however, seem to normalize the practice, and will likely make the problem much more common. This  change also has the effect of reducing the needed keystrokes to alias a function by a factor of two or so.

Brendan Tracey

unread,
Oct 28, 2016, 2:22:31 AM10/28/16
to Robert Griesemer, Dave Cheney, golang-dev
Sorry, by “only work for variables” I meant types.

roger peppe

unread,
Oct 28, 2016, 4:23:14 AM10/28/16
to Dave Cheney, golang-dev
I have yet to see any evidence of the non-readability and incomprehension
that you suggested this feature might cause.

You can (theoretically) as easily jump to the definition of something
that's aliased as something that's not.

Perhaps you could provide some sample code that you think would be made
less readable by the draw changes you mentioned, for example?

I don't see this as a big issue, but I'm open to being convinced otherwise.

cheers,
rog.
> --
> You received this message because you are subscribed to the Google Groups "golang-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Markus Zimmermann

unread,
Oct 28, 2016, 5:41:11 AM10/28/16
to golang-dev
Since the code freeze is in a few days, does this mean that this language change is included in 1.8 unless some really big counterargument is found?

Brian Ketelsen

unread,
Oct 28, 2016, 7:59:36 AM10/28/16
to golang-dev
Code from the draw package for 1.8:

const Over => draw.Over
const Src => draw.Src

func Draw => draw.Draw
func DrawMask => draw.DrawMask

I find this unreadable and confusing.  One of the biggest benefits of Go for me has been its amazingly simple syntax and readability.  Adding aliases with a new operator "=>" adds a layer of indirection and confusion that is unnecessary to the language.  I find this change very disappointing, especially after the continued reassurance in the original proposal that it was only for certain critical edge cases.

image/draw doesn't appear to be either critical or an edge case.  The proliferation of alias usage will make both the standard library and other Go code harder to read and increase the mental load on developers who write and read Go.  

Please reconsider.  If alias must exist, then there should be a very clear guideline for when it can and will be used.  The bar should be set high and documented well.  Better would be removing the "feature" completely as it only adds confusion, cruft and indirection to a language I find to be the most easy to read.

Regards

Brian Ketelsen

atd...@gmail.com

unread,
Oct 28, 2016, 8:01:05 AM10/28/16
to golang-dev


On Friday, October 28, 2016 at 4:25:25 AM UTC+2, Dave Cheney wrote:
Dear all,

I'm writing to formally request that the process of adding aliases to
the language be rolled back.

The more I think about it, the more I think the proposal has merits actually. 
It could probably have been presented a little better but at least it made us think about it.

This proposal allows to build API subsets.
So out of a flat package, it becomes possible to build a tree of somewhat "virtual" packages. That should be interesting in terms of documentation presentation on godoc.

It should also enable some kind of package parametricity. (people could write their datastructures using aliases and would then just have to switch up the aliases definition as needed, still a bit manual but quite more convenient)

Paul Jolly

unread,
Oct 28, 2016, 8:14:36 AM10/28/16
to Brian Ketelsen, golang-dev
Code from the draw package for 1.8:

const Over => draw.Over
const Src => draw.Src

func Draw => draw.Draw
func DrawMask => draw.DrawMask

I find this unreadable and confusing.

Brian, Dave - has this point about readability and comprehension not been made before? Specifically in https://github.com/golang/go/issues/16339?

It is my understanding that this point has been made before, considered by the Go team, but on balance they have concluded that the benefits of aliases outweigh the costs (acknowledging the extent to which they are (un)readable is subjective).

I note however @rsc's response to @egonelbre's excellent suggestion; ideally at this point we could jump straight to the relevant part of the summary to revisit the arguments for/against and the conclusion. Or indeed if, as is possible, I have misunderstood and you are making a new point not previously covered, to have that considered and added to the summary.


Paul


robb...@gmail.com

unread,
Oct 28, 2016, 8:30:49 AM10/28/16
to golang-dev
This change feels like unnecessary syntactic sugar.

It introduces an extra layer of indirection and the new operator will be confusing to newcomers of the language getting to grips with channel syntax.

Regards

Rob Baines

Dominik Honnef

unread,
Oct 28, 2016, 8:50:25 AM10/28/16
to golang-dev
I'd like to point out that we have had weeks of arguments in
https://github.com/golang/go/issues/16339 and that all of these
arguments have been taken into consideration. Simply repeating
arguments that have already been made will not change their weight.
Unless someone has new information that would warrant reevaluating the
choice I don't see much reason for commenting here.

w...@iri-labs.com

unread,
Oct 28, 2016, 8:50:25 AM10/28/16
to golang-dev
Dear all,

I second Dave's request.

The fundamental problem is that aliases create multiple names for the same thing, which is their point.  However, 
nobody wants to see one thing referred to in multiple ways in source code, and I believe this will happen.  The second
problem is readability: the extra layer of indirection would often enough be an extra layer of noise/cognitive overhead
in reading programs.  If I want to understand an algorithm or design, I would have to de-reference aliases to accomplish this.
This seems anti-go to me.   

That said, if it ends up still accepted, please consider:

- It may be more reasonable to allow aliases for the reasons spelled out in the proposal, but to force all code in a package which
contains "alias a => b" to *disallow* using the identifier b in that package outside the context of the alias declaration, which would
help reduce the abovementioned noise but it wouldn't eliminate it.

- Overall, I think aliasing types and constants is much more reasonable than aliasing values.

Thanks,

Scott

Zellyn

unread,
Oct 28, 2016, 9:38:47 AM10/28/16
to golang-dev
The use of aliases for code organization was extensively discussed in the proposal. For example: https://github.com/golang/go/issues/16339#issuecomment-232813695

In particular, there are important cases where this makes things much better for API consumers.

Zellyn

David Norton

unread,
Oct 28, 2016, 9:45:17 AM10/28/16
to golang-dev, da...@cheney.net
Let me say this regarding the fear of misuse: The Go language also implements the goto statement, and it is undisputed that uncontrolled use of gotos is hugely detrimental for readability and comprehension of code. Yet, there is hardly code that abuses gotos these days. But there are rare cases where a goto is exactly the right choice: when you need one (*), you really do.
There's a stigma around goto that prevents its abuse. Aliasing won't have that stigma (at least not for some years). I think in practice we will see complicated aliasing graphs that make refactoring more difficult instead of less difficult. In spite of the advantages of the feature, I agree with Dave.

David
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Bill O'Farrell

unread,
Oct 28, 2016, 10:16:07 AM10/28/16
to golang-dev
My two cents worth: in my role as part of the IBM Hyperledger project, I have been promoting Go as the ideal language for writing "smart contracts" (a.k.a. "chaincode"). Smart contracts, like any contracts, need to be in a language which is comprehensible and clear to all agreeing parties. For this purpose the language should not be contextual -- i.e. changing from one application context to another. Go is simple and concise enough to “keep the whole language in ones head," and deliberately lacks features that can confuse programs and/or programmers. For that reason it is a much better choice then, for example, Java. If aliasing diminishes the context-free clarity of the language then I would have to agree with Dave.



On Thursday, October 27, 2016 at 10:25:25 PM UTC-4, Dave Cheney wrote:

atd...@gmail.com

unread,
Oct 28, 2016, 10:33:47 AM10/28/16
to golang-dev
It's interesting to note that the current proposal enable to refactor an API but not its code.
That's where the confusion may lie.

As such, it is probably much less dangerous than initially thought.

Rob Pike

unread,
Oct 28, 2016, 11:08:04 AM10/28/16
to atd...@gmail.com, golang-dev
Go was always intended for programming at large scale, which includes working in large software environments, with large teams, and in the presence of complicating factors such as interoperating with other languages. The alias feature is directed at solving a problem that arises in that area, which is refactoring a package in an environment too large or complex to update all the dependents of a package at once. This problem is often seen as a versioning problem, but in monorepos versioning doesn't help, while aliases do.

Now, some of Go's popularity is also because of its clarity and readability, but it is worth observing that those properties, which were also goals of the language, are sometimes in conflict with the need for programming at scale. There are other features of the language that are there for industrial-level work but clearly at odds with simplicity. Goto is the most obvious (it's present for machine-generated code). The verbosity of struct literals is another. The way interface embedding works, allowing redundancy if it's not used, is a third. There are more.

What's really different in this conversation is that, for the first time, the community is seeing the difficulty of trying to decide what to do when a new issue raises such a conflict. If we today decided to start enforcing the need to put types on the fields of struct literals, there would be an outcry, but we would do it anyway, because the benefits outweigh the cost, high though the cost is.

The alias feature is important, but it is also ugly. We know that. It is open to abuse. We know that. But we have been listening: the feature is very heavily restricted because of the many good points raised in the issue discussion. The possibility for abuse is greatly restricted, and the conversation that got us here was invaluable. But the conversation is behind us; now the time has come to try it out. If it turns out to be a real problem in practice, there are three months left to roll it back.

But I don't think it will be a problem, because I believe the community will help make sure the abuses don't happen. If you see an alias appear in code where none is necessary, complain about it, publicly. Update golint to yell whenever the feature is used.

Most important, explain to people what it is for: that, like goto, it is a necessary evil for program development in the modern world.

-rob


Russ Cox

unread,
Oct 28, 2016, 11:19:32 AM10/28/16
to Dominik Honnef, golang-dev
I want to say again what I wrote on the issue on September 15:

"""
I wish there could be widespread agreement on this issue about the merits and tradeoff here. That is our ideal for any proposal. But there is not, and after two months of discussion it seems clear that further discussion is not going to change the situation. At this point the best way to learn more is to implement this proposal and try it. We still have until the Go 1.8 release to disable it if a strong reason turns up.

Although the many important voices here are split, Rob, Robert, Ian, and I are not. Along with Ken, who is no longer working on Go on a daily basis, we were responsible for nearly all of the original design of Go, and we all agree, based on our extensive experience with Go and with very large Go code bases, that something along the lines of this proposal is needed. I ask only that you give us the benefit of the doubt on this decision and come along with us to see how it turns out.
"""

We certainly hear the people saying that they think this is making code less readable, or can or will be abused. To those people I can only ask for patience to let this process play out. 

We all have a natural bias to see "different" as "unreadable". I remember very clearly the discussion in Rob's office in January 2009 in which we - Rob, Robert, Ian, Ken, and I - decided to try "upper case means exported" for symbol visibility. I personally thought it was not a good idea and hurt readability, but the proposal addressed a real need (selective export of struct fields), and I could make no technical arguments against it other than I didn't like the way it looked. We did of course try it, and after a few weeks it stopped looking unreadable (that really meant "less like C than Go had before") and became completely natural. Today it is one of my favorite Go features, and I don't think anyone here would argue that it makes the code unreadable. To the contrary, there is actually a technical argument that it enhances readability. I realized this a few years later, when I had to do some work in an unfamiliar section of Google's C++ code base. I found it quite frustrating that I couldn't tell just from looking at a use of a method whether this was a public or private method. I'd see a call and think "wow, is that part of the public API? That would be scary." and have to go dig up the actual method definition and see that oh, it's marked private. In Go, that information is visible at each use, and I'd grown accustomed to having that information implicitly at hand when reading code. So not only was I wrong about the aesthetics of "upper case means exported" - we all adapted just fine to reading Go code written that way - but if we'd let the aesthetics make the decision, we would have cut off this other technical benefit, which we certainly discussed at the time but I think could not fully appreciate the value of until later.

Every language feature can be abused. The fact that Go uses predeclared identifiers instead of keywords when possible makes it easier to extend the language with new builtins without fear or breaking existing programs, like when we added delete. It also lets code dealing with marshalling or unmarshalling have methods with clear names like int and string (see for example go/src/encoding/gob/debug.go). These are good things. It also lets people write code like https://play.golang.org/p/3wrRikamA-, which no one would say is a good thing. We accept the fact that the last example is possible as a consequence of abuse of these more limited good things, and over time we learn conventions about what to do and not do. If you saw code like that playground example anywhere but a compiler test case (it's really go/test/rename.go), you'd encourage the author to write it differently.

Every language feature is overused at first. Experimenting is how we explore and chart the boundaries of what a language feature is and is not good for. The experiments that fail are the ones we later understand as overuse. When Go was first created, the pieces most unique to Go were channels and goroutines, so naturally we emphasized them in our talks about the language and our examples. This led to overuse of channels in particular, and in response we had to develop advice about when not to use channels: Don't use them for lightweight iterators. Don't use them when a plain mutex is sufficient, but do use them when you might use a mutex+condition variable in another language.

Back to the specifics of alias: as I said in September, based on our experience with Go and with very large Go code bases, Rob, Robert, Ian, and I believe that Go needs some kind of indirection mechanism, to enable incremental evolution of and changes to the layout of Go source trees (C/C++ programs use a combination of typedef and #define). Alias has been designed to (we think) harmonize with and be orthogonal to the rest of the language, in the spirit of Go's design, a generalization of typedef without the abuses of #define. The community feedback in the proposal discussion raised important, objective technical suggestions to limit abuse, and in response we introduced (we think) reasonable limits on alias: it can be used only at top level, it can refer only to imported symbols, it cannot refer to package unsafe. The feedback also included some important but more subjective arguments, such as the "detriment of the readability and comprehension" of Go programs. These latter arguments being subjective, all I can say is that we hear you but we disagree that they outweigh the benefits, keeping in mind especially that there is a natural bias to see different as unreadable, especially at first; that every language feature can be abused, so potential for abuse is not by itself disqualifying; and that every language feature is overused at first, as part of the natural process of exploring how it should be used.

To respond to Dave's specific points:

- Robert and others brought up the idea of an environment variable, but we decided against that. GO15VENDOREXPERIMENT was important because the changed meaning of the directory name "vendor" was going to break code that worked in Go 1.4. The environment variable let us use Go 1.5 as the "let people find out what will break and update their code" cycle. But it also created pockets of mutually uninteroperable Go code because some only compiled with GO15VENDOREXPERIMENT=1 and some only compiled without it. It was important to have that transition, but it was also an unfortunate situation. We do not want to repeat that.

- Types are not the only declaration that doesn't have an existing analog. Vars don't either, and while funcs can be worked around, the alias form is significantly cleaner. That leaves consts, for which aliases require one more character than ordinary assignment and have roughly equivalent effect. We could have excluded aliases from consts but it seemed more orthogonal to let them apply to all four instead of three of the four (or, if you argue for excluding funcs too, two of the four).

- As I hope I explained above, aliases are about incremental evolution of and changes to the layout of Go source trees. We see this in a variety of ways in large Go source trees. One common way does involve protocol buffers but is not fundamental to protocol buffers. What we see specifically is engineers doing incremental evolution of and changes to the layout of Google's source tree, in particular to protocol buffer definitions that compile to language-specific libraries. When this happens, C/C++ and Java have no problem accommodating this evolution while Go acts as a hindrance to that evolution. The ability to make this kind of change to a source tree is something C/C++ and Java programmers take for granted and are frustrated not to be able to do in Go, just as seeing the visibility of an identifier at each use is something Go programmers take for granted and are (at least sometimes) frustrated not to be able to do in C/C++ and Java. The specific protocol buffer setting was just the way that C/C++ and Java programmers helped us find this shortcoming of Go. Since Go was designed explicitly for large code bases, this is an important shortcoming to address. This is not a quid pro quo for Google. This is using our experience working in a very large source tree at Google to inform making Go work well for software development at scale. We have taken advantage of this experience throughout the design of Go, and we'll continue to do that. 

- Nigel's use of aliases to make a "draw++" package seems like a good experiment. Maybe we will find that this is a compelling way to evaluate possible evolution of existing packages and end up building tooling to support that use. Maybe we'll find that it's a mistake, like using channels as iterators, and discourage that use. These things are hard to predict. It is certainly too early to say.

This thread asking for the feature to be rolled back started less than nine hours after the compiler support was committed. That is really not enough time to make an informed decision or really even build up an informed opinion about the practical usage of a new language feature. Please be patient and in fact please join us as we experiment to find out what this feature is and is not good for.

Thanks.
Russ

Jessica Frazelle

unread,
Oct 28, 2016, 11:31:44 AM10/28/16
to Russ Cox, Dominik Honnef, golang-dev

On Oct 28, 2016 8:19 AM, "Russ Cox" <r...@golang.org> wrote:
>
> I want to say again what I wrote on the issue on September 15:
>
> """
> I wish there could be widespread agreement on this issue about the merits and tradeoff here. That is our ideal for any proposal. But there is not, and after two months of discussion it seems clear that further discussion is not going to change the situation. At this point the best way to learn more is to implement this proposal and try it. We still have until the Go 1.8 release to disable it if a strong reason turns up.
>
> Although the many important voices here are split, Rob, Robert, Ian, and I are not. Along with Ken, who is no longer working on Go on a daily basis, we were responsible for nearly all of the original design of Go, and we all agree, based on our extensive experience with Go and with very large Go code bases, that something along the lines of this proposal is needed. I ask only that you give us the benefit of the doubt on this decision and come along with us to see how it turns out.
> """
>
> We certainly hear the people saying that they think this is making code less readable, or can or will be abused. To those people I can only ask for patience to let this process play out. 
>
> We all have a natural bias to see "different" as "unreadable". I remember very clearly the discussion in Rob's office in January 2009 in which we - Rob, Robert, Ian, Ken, and I - decided to try "upper case means exported" for symbol visibility. I personally thought it was not a good idea and hurt readability, but the proposal addressed a real need (selective export of struct fields), and I could make no technical arguments against it other than I didn't like the way it looked. We did of course try it, and after a few weeks it stopped looking unreadable (that really meant "less like C than Go had before") and became completely natural. Today it is one of my favorite Go features, and I don't think anyone here would argue that it makes the code unreadable. To the contrary, there is actually a technical argument that it enhances readability. I realized this a few years later, when I had to do some work in an unfamiliar section of Google's C++ code base. I found it quite frustrating that I couldn't tell just from looking at a use of a method whether this was a public or private method. I'd see a call and think "wow, is that part of the public API? That would be scary." and have to go dig up the actual method definition and see that oh, it's marked private. In Go, that information is visible at each use, and I'd grown accustomed to having that information implicitly at hand when reading code. So not only was I wrong about the aesthetics of "upper case means exported" - we all adapted just fine to reading Go code written that way - but if we'd let the aesthetics make the decision, we would have cut off this other technical benefit, which we certainly discussed at the time but I think could not fully appreciate the value of until later.
>
> Every language feature can be abused. The fact that Go uses predeclared identifiers instead of keywords when possible makes it easier to extend the language with new builtins without fear or breaking existing programs, like when we added delete. It also lets code dealing with marshalling or unmarshalling have methods with clear names like int and string (see for example go/src/encoding/gob/debug.go). These are good things. It also lets people write code like https://play.golang.org/p/3wrRikamA-, which no one would say is a good thing. We accept the fact that the last example is possible as a consequence of abuse of these more limited good things, and over time we learn conventions about what to do and not do. If you saw code like that playground example anywhere but a compiler test case (it's really go/test/rename.go), you'd encourage the author to write it differently.
>
> Every language feature is overused at first. Experimenting is how we explore and chart the boundaries of what a language feature is and is not good for. The experiments that fail are the ones we later understand as overuse. When Go was first created, the pieces most unique to Go were channels and goroutines, so naturally we emphasized them in our talks about the language and our examples. This led to overuse of channels in particular, and in response we had to develop advice about when not to use channels: Don't use them for lightweight iterators. Don't use them when a plain mutex is sufficient, but do use them when you might use a mutex+condition variable in another language.
>
> Back to the specifics of alias: as I said in September, based on our experience with Go and with very large Go code bases, Rob, Robert, Ian, and I believe that Go needs some kind of indirection mechanism, to enable incremental evolution of and changes to the layout of Go source trees (C/C++ programs use a combination of typedef and #define). Alias has been designed to (we think) harmonize with and be orthogonal to the rest of the language, in the spirit of Go's design, a generalization of typedef without the abuses of #define. The community feedback in the proposal discussion raised important, objective technical suggestions to limit abuse, and in response we introduced (we think) reasonable limits on alias: it can be used only at top level, it can refer only to imported symbols, it cannot refer to package unsafe. The feedback also included some important but more subjective arguments, such as the "detriment of the readability and comprehension" of Go programs. These latter arguments being subjective, all I can say is that we hear you but we disagree that they outweigh the benefits, keeping in mind especially that there is a natural bias to see different as unreadable, especially at first; that every language feature can be abused, so potential for abuse is not by itself disqualifying; and that every language feature is overused at first, as part of the natural process of exploring how it should be used.
>
> To respond to Dave's specific points:
>
> - Robert and others brought up the idea of an environment variable, but we decided against that. GO15VENDOREXPERIMENT was important because the changed meaning of the directory name "vendor" was going to break code that worked in Go 1.4. The environment variable let us use Go 1.5 as the "let people find out what will break and update their code" cycle. But it also created pockets of mutually uninteroperable Go code because some only compiled with GO15VENDOREXPERIMENT=1 and some only compiled without it. It was important to have that transition, but it was also an unfortunate situation. We do not want to repeat that.
>
> - Types are not the only declaration that doesn't have an existing analog. Vars don't either, and while funcs can be worked around, the alias form is significantly cleaner. That leaves consts, for which aliases require one more character than ordinary assignment and have roughly equivalent effect. We could have excluded aliases from consts but it seemed more orthogonal to let them apply to all four instead of three of the four (or, if you argue for excluding funcs too, two of the four).
>
> - As I hope I explained above, aliases are about incremental evolution of and changes to the layout of Go source trees. We see this in a variety of ways in large Go source trees. One common way does involve protocol buffers but is not fundamental to protocol buffers. What we see specifically is engineers doing incremental evolution of and changes to the layout of Google's source tree, in particular to protocol buffer definitions that compile to language-specific libraries. When this happens, C/C++ and Java have no problem accommodating this evolution while Go acts as a hindrance to that evolution. The ability to make this kind of change to a source tree is something C/C++ and Java programmers take for granted and are frustrated not to be able to do in Go, just as seeing the visibility of an identifier at each use is something Go programmers take for granted and are (at least sometimes) frustrated not to be able to do in C/C++ and Java. The specific protocol buffer setting was just the way that C/C++ and Java programmers helped us find this shortcoming of Go. Since Go was designed explicitly for large code bases, this is an important shortcoming to address. This is not a quid pro quo for Google. This is using our experience working in a very large source tree at Google to inform making Go work well for software development at scale. We have taken advantage of this experience throughout the design of Go, and we'll continue to do that. 
>
> - Nigel's use of aliases to make a "draw++" package seems like a good experiment. Maybe we will find that this is a compelling way to evaluate possible evolution of existing packages and end up building tooling to support that use. Maybe we'll find that it's a mistake, like using channels as iterators, and discourage that use. These things are hard to predict. It is certainly too early to say.
>
> This thread asking for the feature to be rolled back started less than nine hours after the compiler support was committed. That is really not enough time to make an informed decision or really even build up an informed opinion about the practical usage of a new language feature. Please be patient and in fact please join us as we experiment to find out what this feature is and is not good for.

I think the fear stems from the fact it's already been merged. In my experience, it's harder and harder to back out things once merged. Stuff gets merged on top and as time ticks by it seems like the only way this will be reverted is if it's harming kittens or something obscene. Obviously that won't be the case as it's more just a general dislike from a lot of people so I truly think the reality is setting in.

I stand with Dave.

Bakul Shah

unread,
Oct 28, 2016, 11:35:07 AM10/28/16
to Rob Pike, atd...@gmail.com, golang-dev
Once all the files using some aliased objects are updated, may be a "refactor" tool can clean up by moving aliased objects in the right place? It would read a file indicating what aliases are to be removed. Alternatively it can warn you which files are affected. Without something like this I fear code facilitating refactoring can stick around forever. 

Asif Jalil

unread,
Oct 28, 2016, 11:56:41 AM10/28/16
to golang-dev
The goal of the alias proposal is this: 

Aliases simplify splitting packages because clients can be updated incrementally, which is crucial for large-scale refactoring.

To me, it seems clear when the aliases should be used, and also seems like the easiest and cleanest way to do refactoring in Go. I would rather have the ability to easily refactor code over potential indirection and confusion because a tool like Guru can help me with the indirection. But without the aliases, I can't easily refactor my code.

Asif

On Thursday, October 27, 2016 at 10:25:25 PM UTC-4, Dave Cheney wrote:

co...@lanou.com

unread,
Oct 28, 2016, 11:56:41 AM10/28/16
to golang-dev
A few of us on our team discussed the topic this morning.  I'm a bit dimwitted, so it took a while for me to understand the need, but they took the time to talk slow and spell it out for me.

Many of us on our team feel that while there is merit in it's use for refactoring (and we would benefit from that on our project), it comes at to high of a cost.  For one, we feel that the removal of the aliasing after a refactor likely won't happen, and the cruft will now live in the code forever.

Additionally, you are now going to live with the following insanity from package maintainers:

package L2


func
Foo() {
 println
("foo")
}

package L1


import (
 
"github.com/corylanou/tmp/l2
)

func Foo => L2.Foo


package L


import (
 
"github.com/corylanou/tmp/l1
)

func Foo => L1.Foo


Can we at least hold off on making this a part of the langauge in 1.8 until there is more discussion?  And yes, I know this has all been brought up in the issue already.

Peter Bourgon

unread,
Oct 28, 2016, 12:11:01 PM10/28/16
to Rob Pike, atd...@gmail.com, golang-dev
On Fri, Oct 28, 2016 at 5:07 PM, Rob Pike <r...@golang.org> wrote:
> Go was always intended for programming at large scale, which includes
> working in large software environments, with large teams, and in the
> presence of complicating factors such as interoperating with other
> languages. The alias feature is directed at solving a problem that arises in
> that area, which is refactoring a package in an environment too large or
> complex to update all the dependents of a package at once. This problem is
> often seen as a versioning problem, but in monorepos versioning doesn't
> help, while aliases do.
>
> Now, some of Go's popularity is also because of its clarity and readability,
> but it is worth observing that those properties, which were also goals of
> the language, are sometimes in conflict with the need for programming at
> scale. There are other features of the language that are there for
> industrial-level work but clearly at odds with simplicity. Goto is the most
> obvious (it's present for machine-generated code). The verbosity of struct
> literals is another. The way interface embedding works, allowing redundancy
> if it's not used, is a third. There are more.
>
> What's really different in this conversation is that, for the first time,
> the community is seeing the difficulty of trying to decide what to do when a
> new issue raises such a conflict. If we today decided to start enforcing the
> need to put types on the fields of struct literals, there would be an
> outcry, but we would do it anyway, because the benefits outweigh the cost,
> high though the cost is.
>
> The alias feature is important,

The case for this statement has unfortunately not been made convincingly.

I stand with Dave. Please don't move forward with this feature.


> but it is also ugly. We know that. It is
> open to abuse. We know that. But we have been listening: the feature is very
> heavily restricted because of the many good points raised in the issue
> discussion. The possibility for abuse is greatly restricted, and the
> conversation that got us here was invaluable. But the conversation is behind
> us; now the time has come to try it out. If it turns out to be a real
> problem in practice, there are three months left to roll it back.
>
> But I don't think it will be a problem, because I believe the community will
> help make sure the abuses don't happen. If you see an alias appear in code
> where none is necessary, complain about it, publicly. Update golint to yell
> whenever the feature is used.
>
> Most important, explain to people what it is for: that, like goto, it is a
> necessary evil for program development in the modern world.
>
> -rob
>
>

john...@gmail.com

unread,
Oct 28, 2016, 12:42:11 PM10/28/16
to golang-dev
Wow, I get up Friday morning and discover that there's this huge thread  about a feature I had no idea was being considered. At one time, this wouldn't have happened: features like this would have had at least a preliminary design document posted someplace I could find them. However, posting to the wiki seems to have been discontinued several releases ago, and I have no idea where, or even whether, it's possible to find them.

It would be helpful for people like me who are not involved with the day-to-day work of the Go development process to have a way of finding this kind of change proposal so we'd have time to think it through, or at least adjust to it.

This is a bit frustrating, because I keep my eye on what's being proposed for Python. Nothing gets into that language without a formal proposal (called a PEP for Python Enhancement Proposal), which is numbered and posted in a standard location. The practice mentioned above served the same purpose.

That said, if I understand the discussion to this point, the primary use case is to allow a quick, minimally expensive in developer time, fixup when a project's import tree changes under it. I presume there are reasons why standard refactoring tools aren't appropriate in this case.




Brad Fitzpatrick

unread,
Oct 28, 2016, 12:44:23 PM10/28/16
to john...@gmail.com, golang-dev

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.

murali

unread,
Oct 28, 2016, 1:03:03 PM10/28/16
to golang-dev
I find this quite convincing to me than the one regarding large-scale refactoring (they appear to be the two sides of the same coin though).

By the way, I wonder if aliases would make #11647 more feasible.

Egon Elbre

unread,
Oct 28, 2016, 1:16:08 PM10/28/16
to golang-dev, atd...@gmail.com


On Friday, 28 October 2016 08:08:04 UTC-7, Rob Pike wrote:
Go was always intended for programming at large scale, which includes working in large software environments, with large teams, and in the presence of complicating factors such as interoperating with other languages. The alias feature is directed at solving a problem that arises in that area, which is refactoring a package in an environment too large or complex to update all the dependents of a package at once. This problem is often seen as a versioning problem, but in monorepos versioning doesn't help, while aliases do.

Now, some of Go's popularity is also because of its clarity and readability, but it is worth observing that those properties, which were also goals of the language, are sometimes in conflict with the need for programming at scale. There are other features of the language that are there for industrial-level work but clearly at odds with simplicity. Goto is the most obvious (it's present for machine-generated code). The verbosity of struct literals is another. The way interface embedding works, allowing redundancy if it's not used, is a third. There are more.

What's really different in this conversation is that, for the first time, the community is seeing the difficulty of trying to decide what to do when a new issue raises such a conflict. If we today decided to start enforcing the need to put types on the fields of struct literals, there would be an outcry, but we would do it anyway, because the benefits outweigh the cost, high though the cost is.

The alias feature is important, but it is also ugly. [snip]

I think this is the wrong wording, the correct would be 

"Refactoring in a big code-base is important -- and alias feature is the best solution we came up with".

The problem I see with alias is that I cannot see how it helps refactoring nor are there proper real-world examples in the design document how it helps refactoring. So, what is the exact refactoring workflow?

Most of the useful "refactorings" I do are type splitting and merging; adding/removing/replacing fields and name changes... it helps with moving things between packages -- but type-splitting/merging are more useful.

The splitting of packages is an interesting use-case ... but yet again I failed to find a proper case-study. The best I saw was few comments how it would help some package -- but I didn't understand how.

Because I cannot really see how it would work and/or the good use-cases... I stand with Dave.

What I would like to see:
1. this is the starting situtation: (e.g. we have 4 teams of programmers, each having 5 programmers; all are using git except X,Y,Z; and we use codereview plugin)
2. this is the starting state of the code base: (e.g. here we have a struct field that needs to be renamed/removed)
3. here is Team1:Person2 making a CL to remove the struct field
4. here is other teams making conflicting CL-s
5. ....
N. all is finally in a consistent state with the struct field removed. With a few pending CL-s for Q and R.

Hopefully having this alias feature will make it easier to produce such use-cases...

(And please no facilitated examples, facilitated examples for work poorly because it's impossible to tell whether the thing you are trying to do is a good idea in the first place. Also, make it a Google Doc -- threads are not suitable for it.)

// PS: I feel like this whole thread will deteriorate again without a proper document to write up things. Go Packaging Proposal Process (https://docs.google.com/document/d/18tNd8r5DV0yluCR7tPvkMTsWD_lYcRO7NhpNSDymRr8/edit) is a good example how to do it.

+ Egon

Robert Griesemer

unread,
Oct 28, 2016, 1:27:53 PM10/28/16
to golang-dev
Hello everybody;

Almost every single comment in this e-mail thread has been discussed in detail in the proposal, which has been out for more than 3 months now.

If you're new to this discussion, or if you have any new insight, comment, or other relevant opinion, please see and comment at


--

roger peppe

unread,
Oct 28, 2016, 1:32:05 PM10/28/16
to golang-dev
I honestly don't think this will hurt readability much,
and I care deeply about readability.

I have however just realised what feels odd to me about the
specific syntax:

type A => B

makes it look like A flows to B, but actually the flow is the
other way.

type A <= B

looks odd though.

I kinda wish it had been decided to go with type aliases only.

type A = B

because mutable global variables are almost always a smell, and
functions and constants are trivial to do already.

Rob Pike

unread,
Oct 28, 2016, 1:40:27 PM10/28/16
to roger peppe, golang-dev
This too was talked about. One person's flow (information flows from B to A) is another's reference (A refers to B). Either makes sense, but the right-pointing arrow seemed consistent with other things in our universe, such as how symlinks are presented by ls -l on Unix. And since an alias is analogous to a symlink, that seemed the right choice.

-rob


Chris Hines

unread,
Oct 28, 2016, 1:45:27 PM10/28/16
to golang-dev, atd...@gmail.com
Although my mind remains open, Egon has perfectly expressed my current thoughts on the aliasing feature. I read the original proposal and followed the discussion for several weeks after. Since then I have not followed the work as closely. I've seen several creative uses for aliases tossed out, but I have yet to fully understand the intended workflow for their originally declared purpose. I have not passed judgement on aliases yet, but like Egon, I would like to see a detailed walkthrough of an alias enabled refactoring on a real code base documented during this trial period. I think it would help everyone involved to see something concrete.

Chris 

Jaana Burcu Dogan

unread,
Oct 28, 2016, 2:09:49 PM10/28/16
to roger peppe, golang-dev
On Fri, Oct 28, 2016 at 10:31 AM, roger peppe <rogp...@gmail.com> wrote:
I honestly don't think this will hurt readability much,
and I care deeply about readability.


I agree with this. There are already many opportunities to create indirection in Go, but we have been historically good at calling them out.

type I interface{}

I cannot remember how many times I have seen people giving feedback against this particular type def or creating unnecessary shortcuts such as,

var TODO = context.TODO()

As long as we communicate aliases well, I don't believe they will automatically become a disaster for readability.
 
I have however just realised what feels odd to me about the
specific syntax:

   type A => B

makes it look like A flows to B, but actually the flow is the
other way.

   type A <= B

looks odd though.

I kinda wish it had been decided to go with type aliases only.

   type A = B

because mutable global variables are almost always a smell, and
functions and constants are trivial to do already.

Vitor De Mario

unread,
Oct 28, 2016, 2:12:20 PM10/28/16
to Chris Hines, golang-dev, atd...@gmail.com
One question: if aliases are in by November 1st can we assume they're here to stay and will be a part of Go 1.8? Or is it possible they're removed at some point during the freeze with the code that uses it needing to roll back the changes?

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Pietro Gagliardi

unread,
Oct 28, 2016, 2:12:26 PM10/28/16
to golang-dev
Does anyone here have proof that the existence of an alias feature **WILL** lead to abuse? Because all I've been seeing in this thread is people who are afraid it **MIGHT**.

I agree that it is important to remove features that can be abused. But the Go community doesn't have a history that I can think of of abusing features. THere's no real precedent that says "we cannot trust Go programmers to be stupid". Compare this to, say, Microsoft, who denies programmatic access to new Windows features if similar old features have had a long history of abuse in the past (see https://blogs.msdn.microsoft.com/oldnewthing/20030903-00/?p=42673).

So what is the empirical proof that a) this feature **WILL** be abused, and b) is it really abuse?

>
> On Oct 28, 2016, at 12:42 PM, john...@gmail.com wrote:
>
> It would be helpful for people like me who are not involved with the day-to-day work of the Go development process to have a way of finding this kind of change proposal so we'd have time to think it through, or at least adjust to it.
>
https://github.com/golang/proposal

Axel Wagner

unread,
Oct 28, 2016, 2:13:37 PM10/28/16
to Chris Hines, golang-dev, atd...@gmail.com
Again, the perfect example of where the aliasing feature helps for refactoring, is the move of golang.org/x/net/context to context/. The specific workflow for a case like this (not this particular case, as it's already too late for that) is:

a) Create the context/ package, with all the code. In the same commit, add aliases in x/net/context for all types and functions to refer to context/
b) Give a long deprecation phase for people to change their references from x/net/context directly to context/
c) After (almost) all references are changed, delete x/net/context

This creates a path, where all code in the universe remains buildable and mutually compatible, no matter to which package it refers to. This is currently not possible otherwise, because if package a defines

type Foo func(context.Context)

and package b has a

func Bar(context.Context)

you'll need to update both A and B simultaneously from x/net/context to context/ to be able to use b.Bar as an a.Foo, which is usually not feasible (because, e.g., they have different release cycles).

This is the "large scale refactoring" problem that is referred to and that aliases provide a solution for. This has also been described very often in the issue and discussion so far.

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.

Axel Wagner

unread,
Oct 28, 2016, 2:15:02 PM10/28/16
to Vitor De Mario, Chris Hines, golang-dev, atd...@gmail.com
This has been answered by Rob implicitly, when he said "there are still three month to roll it back". So, yes, it seems it may be rolled back after the freeze too.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.

Egon Elbre

unread,
Oct 28, 2016, 2:26:35 PM10/28/16
to golang-dev, ggr...@cs-guy.com, atd...@gmail.com
On Friday, 28 October 2016 11:13:37 UTC-7, Axel Wagner wrote:
Again, the perfect example of where the aliasing feature helps for refactoring, is the move of golang.org/x/net/context to context/. The specific workflow for a case like this (not this particular case, as it's already too late for that) is:

a) Create the context/ package, with all the code. In the same commit, add aliases in x/net/context for all types and functions to refer to context/
b) Give a long deprecation phase for people to change their references from x/net/context directly to context/
c) After (almost) all references are changed, delete x/net/context

In theory this could have also been done with a symlink (assuming we get them to work across all OS-s and setups)?

This creates a path, where all code in the universe remains buildable and mutually compatible, no matter to which package it refers to. This is currently not possible otherwise, because if package a defines

type Foo func(context.Context)

and package b has a

func Bar(context.Context)

Please, no facilitiated examples... it is impossible to tell whether Foo and Bar are good ideas in the first place. I know it's a convenient shortcut for discussions, but they trivialize real problems.


you'll need to update both A and B simultaneously from x/net/context to context/ to be able to use b.Bar as an a.Foo, which is usually not feasible (because, e.g., they have different release cycles).

This is the "large scale refactoring" problem that is referred to and that aliases provide a solution for. This has also been described very often in the issue and discussion so far.

Thank you, I failed to find this clear example in the discussion.

Were there any examples for other types (and examples) of refactorings that I missed?


To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Axel Wagner

unread,
Oct 28, 2016, 3:21:05 PM10/28/16
to Egon Elbre, golang-dev, Chris Hines, atd...@gmail.com
On Fri, Oct 28, 2016 at 8:26 PM, Egon Elbre <egon...@gmail.com> wrote:
On Friday, 28 October 2016 11:13:37 UTC-7, Axel Wagner wrote:
Again, the perfect example of where the aliasing feature helps for refactoring, is the move of golang.org/x/net/context to context/. The specific workflow for a case like this (not this particular case, as it's already too late for that) is:

a) Create the context/ package, with all the code. In the same commit, add aliases in x/net/context for all types and functions to refer to context/
b) Give a long deprecation phase for people to change their references from x/net/context directly to context/
c) After (almost) all references are changed, delete x/net/context

In theory this could have also been done with a symlink (assuming we get them to work across all OS-s and setups)?

No. Unless the compiler would know about it and assume both packages to be equal. It would also make the confusion even worse; now you can not see from the source at all that there is aliasing happening and will end up with two packages referring to the same package by different names. Imagine getting an error message for foo/bar/baz/main.go, but finding this string in your source, because spam/eggs has been aliased to it and you are importing that instead.

It would also not solve moving single types between packages, but only whole packages as a unit.

This creates a path, where all code in the universe remains buildable and mutually compatible, no matter to which package it refers to. This is currently not possible otherwise, because if package a defines

type Foo func(context.Context)

and package b has a

func Bar(context.Context)

Please, no facilitiated examples... it is impossible to tell whether Foo and Bar are good ideas in the first place. I know it's a convenient shortcut for discussions, but they trivialize real problems.

The level of abstraction to go from this to something realistic is very low. As a specific example where this specific problem is happening right now, see
The same issue will also happen in all kinds of software that uses context in an interface or function type. As it's usage should be quite pervasive in networking code, I suggest looking at various web- or microservices frameworks/toolkits for it.

atd...@gmail.com

unread,
Oct 28, 2016, 3:32:32 PM10/28/16
to golang-dev, ggr...@cs-guy.com, atd...@gmail.com
I am not so sure that it is the exact workflow/use-case.
It could easily break reflection for instance.

It's not often possible to split a package physically (into different packages). The alias proposal enables the splitting of the API surface however..

It would enable incremental extension of a subset of an API independently for instance. But once a package has a given API, it shouldn't be reduced (for backward compatibility).

It's also not possible to replace a type by an alias.

atd...@gmail.com

unread,
Oct 28, 2016, 3:48:21 PM10/28/16
to golang-dev, ggr...@cs-guy.com, atd...@gmail.com
To be clearer, aliases allow to create a new package with a an API subset, but the use case does not include the modification of an old package unless it is to extend the API.

Because a composite type definition may involve unexported types, composite types can easily get tied to a given package name. Which leads me to think that they cannot be replaced by aliases or moved in the general case.

So, the refactoring use-case really seems to be about encouraging the incremental evolution of newly created APIs (carved out of old ones)

That's at least my understanding.

Egon Elbre

unread,
Oct 28, 2016, 3:56:41 PM10/28/16
to golang-dev, egon...@gmail.com, ggr...@cs-guy.com, atd...@gmail.com
On Friday, 28 October 2016 12:21:05 UTC-7, Axel Wagner wrote:


On Fri, Oct 28, 2016 at 8:26 PM, Egon Elbre <egon...@gmail.com> wrote:
On Friday, 28 October 2016 11:13:37 UTC-7, Axel Wagner wrote:
Again, the perfect example of where the aliasing feature helps for refactoring, is the move of golang.org/x/net/context to context/. The specific workflow for a case like this (not this particular case, as it's already too late for that) is:

a) Create the context/ package, with all the code. In the same commit, add aliases in x/net/context for all types and functions to refer to context/
b) Give a long deprecation phase for people to change their references from x/net/context directly to context/
c) After (almost) all references are changed, delete x/net/context

In theory this could have also been done with a symlink (assuming we get them to work across all OS-s and setups)?

No. Unless the compiler would know about it and assume both packages to be equal. It would also make the confusion even worse; now you can not see from the source at all that there is aliasing happening and will end up with two packages referring to the same package by different names. Imagine getting an error message for foo/bar/baz/main.go, but finding this string in your source, because spam/eggs has been aliased to it and you are importing that instead.

I agree.
 
It would also not solve moving single types between packages, but only whole packages as a unit.

Yes, the workflow suggested that something smaller was sufficient. In the similar vein symlinks also do not solve type splitting.
 

This creates a path, where all code in the universe remains buildable and mutually compatible, no matter to which package it refers to. This is currently not possible otherwise, because if package a defines

type Foo func(context.Context)

and package b has a

func Bar(context.Context)

Please, no facilitiated examples... it is impossible to tell whether Foo and Bar are good ideas in the first place. I know it's a convenient shortcut for discussions, but they trivialize real problems.

The level of abstraction to go from this to something realistic is very low.

Only if you know the real-world examples already. If you don't know a good example what you are looking for, you cannot find them.
 
As a specific example where this specific problem is happening right now, see

This is a good example.

br...@buddin.us

unread,
Oct 28, 2016, 4:11:03 PM10/28/16
to golang-dev
I don't have so much of a problem with aliasing in general, because I see plenty of indirection of underlying value all the time. Whether or not the specific usage of indirection is good or not is up to the situation. However, the right arrow (->) syntax seems problematic. Other than the `{ }` characters, Go usually reserves some series of special characters with a singular meaning—or at least something that is at least related/congruent with other usage contexts.

Brad Fitzpatrick

unread,
Oct 28, 2016, 4:17:32 PM10/28/16
to br...@buddin.us, golang-dev
On Fri, Oct 28, 2016 at 1:03 PM, <br...@buddin.us> wrote:
I don't have so much of a problem with aliasing in general, because I see plenty of indirection of underlying value all the time. Whether or not the specific usage of indirection is good or not is up to the situation. However, the right arrow (->) syntax seems problematic.

That's not the syntax.

 

john...@gmail.com

unread,
Oct 28, 2016, 4:36:01 PM10/28/16
to golang-dev, john...@gmail.com
My comment wasn't so much about whether it went through the process; I'm confident that the Go team is rather strict about following process. It was that I didn't know where to find the proposal documents. It's not on the "Project" page as far as I can tell. I only found the proposal document when I got pointed to the issue repository, and then only because a pointer was included in the issue. That's not a repository that I normally look at.

As far as the actual proposal goes; it's the high quality I expect of the Go team. I've been in situations where refactoring the way a large-scale system with a lot of moving parts is needed, and it's a royal PITA, even when it doesn't involve anyone outside the org. I'd think that first use case would be welcomed with open arms, especially if aliases were somehow marked "for export only." It's the ability to use them internally to a package that seems to be generating all the heat.

On the other hand, there are a lot of "dangerous" facilities that are well handled by team conventions or standards; I don't see any reason why this couldn't be handled the same way.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Nigel Tao

unread,
Oct 28, 2016, 6:35:39 PM10/28/16
to robb...@gmail.com, golang-dev
On Fri, Oct 28, 2016 at 11:19 PM, <robb...@gmail.com> wrote:
> This change feels like unnecessary syntactic sugar.

I have updated the CL to emphasize that it is not merely syntactic
sugar. Copy/pasting from
https://go-review.googlesource.com/#/c/32145/4/draw/go1_8.go

----
// We use alias declarations for the exported names from the standard library's
// image/draw package. This is not merely syntactic sugar for
//
// var Draw = draw.Draw
//
// as aliasing means that the types in this package, such as draw.Image and
// draw.Op, are indistinguishable from the corresponding draw.Image and draw.Op
// types in the standard library. In comparison, prior to Go 1.8, the code in
// go1_7.go defines new types that mimic the old but are distinguished types.
//
// The package documentation, in draw.go, explicitly gives the intent of this
// package:
//
// This package is a superset of and a drop-in replacement for the
// image/draw package in the standard library.
//
// Drop-in replacement means that I can replace all of my "image/draw" imports
// with "golang.org/x/image/draw", to access additional features in this
// package, and no further changes are required. That's mostly true, but not
// completely true unless we use type aliases.
//
// Without type aliases, users might need to import both "image/draw" and
// "golang.org/x/image/draw" in order to convert from two conceptually
// equivalent but distinguished (from the compiler's point of view) types, such
// as from one draw.Op type to another draw.Op type, to satisfy some other
// interface or function signature.
----

and from the CL discussion at https://go-review.googlesource.com/#/c/32145/

----
I have updated go1_8.go with the rationale. This is not a 'gee whiz'
change to play with a new toy.

As I said earlier, it is not merely a theoretical concern. It has come
up in practice in
https://go.googlesource.com/mobile/+/master/exp/sprite/portable/portable.go

As I also said earlier, I acknowledge that the inconvenience in
particular for image/draw versus x/image/draw is relatively minor,
compared to the cost of a new language feature, but this inconvenience
is not why the new language feature was introduced. Given that we have
a new language feature, though, this CL improves x/image/draw by
removing that inconvenience.
----

thwd

unread,
Nov 2, 2016, 10:13:12 AM11/2/16
to golang-dev
Hi Robert and team

I read through it all and would like to bring a thought to your attention, one that Marcelo Magallon (mem) also seems to have had:


Specifically, I'm refering to his second bullet point -- the fact that named interface types can already be used interchangeably when their definition is equal.

Axel Wagner (Merovius) then brought a good counter-argument, here:


Namely, that other composite types (like function-types and other interfaces) that mention a named interface type in their definition are not interchangeable when they don't use the same name.

Have you as a team thought about that maybe the problem we're solving through aliasing is actually just a consequence of the impedance between interface-covariance and primitive-invariance and should arguably be fixed in the type system?

Lastly, and in my own opinion, that distinction (covariance vs invariance) is being opaqued by the aliasing feature (I can pass an int to a function expecting an AliasToInt), as if int somehow "implemented" AliasToInt (and vice versa).

Best regards,
Tom 
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

nice...@gmail.com

unread,
Nov 2, 2016, 11:44:36 AM11/2/16
to golang-dev, dom...@honnef.co
based on our extensive experience with Go and with very large Go code bases, that something along the lines of this proposal is needed. I ask only that you give us the benefit of the doubt on this decision and come along with us to see how it turns out.

Instead of giving you the benefit of the doubt, how about some bullets outlining concrete examples of how this will benefit all of us who also have extensive experience with Go but who don't see the same need? 

nice...@gmail.com

unread,
Nov 2, 2016, 11:53:48 AM11/2/16
to golang-dev, atd...@gmail.com
> This problem is often seen as a versioning problem, but in monorepos versioning doesn't help, while aliases do.

Then this is feature because of how _Google_ structures its codebase? I think almost no one else in the world does it that way so it’s a feature solely for Google?.

Ian Lance Taylor

unread,
Nov 2, 2016, 12:09:22 PM11/2/16
to nice...@gmail.com, golang-dev, dom...@honnef.co
Several examples have already been given in the proposal discussion
and in mailing list discussions. These examples are inevitably
complex. The nature of the problem is that it only becomes visible
when dealing with a complex set of packages. In simple cases simple
solutions are fine.

For a concrete example, see the move of golang.org/x/net/context into
the standard library as context. Because the context package is
widely used, and it is difficult to convert all users of the package
at once, it is desirable to permit packages using
golang.org/x/net/context to interoperate with packages that have
converted to context.

I want to stress that this is the crux of the refactoring argument.
You have a lot of packages using some package P1, and those packages
are not all under your control or can not all be modified
simultaneously. You want to move package P1 to P2, or split up
package P1 into multiple packages, or assemble multiple packages into
P2. These are all reasonable actions to take when dealing with a
large system. You don't always know the best package layout as you
start working. In order for Go to be a good language to use for large
evolving systems, it should be able to support this kind of
refactoring without considerable pain and without requiring all
packages using P1 to be modified simultaneously.

If this were a problem that arose in simple situations, we would have
seen it early in the development of the Go language. We have actually
discussed type aliases since before Go was publicly announced. But
there never seemed to be a compelling reason to introduce them to the
language. It was not until years of work with Go, and development of
large systems, and the need to rearrange packages in those large
systems without changing everything at once, that the need for aliases
became clear.

Returning to the context example, to point out the issue as clearly as
I can, there is a problem. The golang.org/x/net/context package
defines a type Context. The new standard library package context
obviously defines the same type. The types are interface types that
implement the same methods, but they are not, of course, the same
type. A package Q1 that uses golang.org/x/net/context may have a type
with a method that returns a Context value; this is a normal
operation. A different package Q2 that uses context may have an
interface with a method of the same name that returns a Context. The
type in Q1 can not be assigned to the interface in Q2, because they
use different Context types. This is not, by the way, a matter of
covariant returns. This is a matter of type identity. Both types are
interface types, they are just different interface types.
Conceptually, since the standard library package context is just a
rename of golang.org/x/net/context, it should be possible to assign
the Q1 type to the Q2 interface. But it is not.

You can reject this example as overly complex. As I said earlier,
these problems only arise in complex situations. But they do arise,
and they must be solved. There are various awkward solutions.
Aliases are intended to be a simple solution. The simplicity can be
seen in the file
https://github.com/golang/net/blob/master/context/go18.go, which uses
aliases as currently expected to be in Go 1.8.

Some people have suggested that this is a problem that only Google
encounters. Google does have a lot of Go code, and does have a lot of
complex packages, and this problem does arise at Google. But Google
also has the ability to change all the code at once, and that is
exactly how we have been dealing with the problem internally. But it
is an awkward solution, not a good one. And it does not help the open
source community, where changing all packages at once is essentially
impossible.

Ian

Ian Lance Taylor

unread,
Nov 2, 2016, 12:19:08 PM11/2/16
to nice...@gmail.com, golang-dev, atd...@gmail.com
This is not a Google-specific problem.

Everybody who uses vendoring to assemble a disparate collection of
packages into a single GOPATH is using a monorepo. Using versioning
to use different versions of the same package for different users of
that package within a single vendor tree is what I would describe as a
very awkward solution to the problem.

For packages developed internally at Google, Google can, and does,
change all the packages at once. It's not the ideal solution, but
it's doable.

Ian

Ian Lance Taylor

unread,
Nov 2, 2016, 12:34:45 PM11/2/16
to thwd, golang-dev
On Wed, Nov 2, 2016 at 7:13 AM, thwd <sedevel...@gmail.com> wrote:
>
> I read through it all and would like to bring a thought to your attention,
> one that Marcelo Magallon (mem) also seems to have had:
>
> https://github.com/golang/go/issues/16339#issuecomment-236439462
>
> Specifically, I'm refering to his second bullet point -- the fact that named
> interface types can already be used interchangeably when their definition is
> equal.
>
> Axel Wagner (Merovius) then brought a good counter-argument, here:
>
> https://github.com/golang/go/issues/16339#issuecomment-236443704
>
> Namely, that other composite types (like function-types and other
> interfaces) that mention a named interface type in their definition are not
> interchangeable when they don't use the same name.
>
> Have you as a team thought about that maybe the problem we're solving
> through aliasing is actually just a consequence of the impedance between
> interface-covariance and primitive-invariance and should arguably be fixed
> in the type system?
>
> Lastly, and in my own opinion, that distinction (covariance vs invariance)
> is being opaqued by the aliasing feature (I can pass an int to a function
> expecting an AliasToInt), as if int somehow "implemented" AliasToInt (and
> vice versa).

Not to miss the obvious, but that would only solve the solution for
interface types. And, unless you further extend covariance to permit
assignment between different interface types with similar method sets,
it would only solve the problem for assigning non-interface types to
interface types.

I personally am not strongly to permitting covariant types in
interface method assignment, but I've struggled with how to implement
them efficiently when converting one interface type to another. It's
not a trivial problem.

It would also make Go's type system, which is currently very simple,
significantly more complicated. Covariant types don't obviously stop
at one level, and they don't obviously stop at interface types.

func F1(func () interface{})
func F2() int

Can I write F1(F2)? If we permit covariant returns in interfaces, why
shouldn't we permit this? How can that be implemented efficiently?

Ian

Egon Elbre

unread,
Nov 2, 2016, 12:46:35 PM11/2/16