Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spec: allow 'any' for 'interface{}' in non-constraint contexts #33232

Closed
Lexkane opened this issue Jul 22, 2019 · 88 comments
Closed

spec: allow 'any' for 'interface{}' in non-constraint contexts #33232

Lexkane opened this issue Jul 22, 2019 · 88 comments

Comments

@Lexkane
Copy link

@Lexkane Lexkane commented Jul 22, 2019

Note, 2021-09-02: Now that any is an alias for interface{}, we have a special case that it can only be used in type parameter constraints. This proposal is now to remove that special case, allowing any, well, anywhere.

This CL shows what it would look like if used throughout the Go tree.

- rsc


I would lilke to be able to declare any type with keyword any instead of empty interface, like rune means alias int32.
Such feature make language more readable , without polluting declarations with many empty interfaces. Imho any keyword is perfect for such alias.

@alanfo
Copy link

@alanfo alanfo commented Jul 22, 2019

I often use a type alias for this which isn't much extra work:

type any = interface{}

Loading

@ianlancetaylor ianlancetaylor changed the title Proposal Go2: Add alias for interface {} as any proposal: Go 2: add alias for interface {} as any Jul 22, 2019
@gopherbot gopherbot added this to the Proposal milestone Jul 22, 2019
@ccbrown
Copy link

@ccbrown ccbrown commented Jul 23, 2019

I agree with the sentiment that interface{} is terrible for readability, but I'm really hoping Go 2 has good enough generics to make nearly all uses of interface{} an avoidable anti-pattern. If that happens, an any type probably wouldn't be warranted at all. But if for some reason generics just completely fall through, sure, I'd support a builtin any alias. I do agree that it would increase readability.

Loading

@dotaheor
Copy link

@dotaheor dotaheor commented Jul 23, 2019

I support this proposal, though not strongly.

In the early days of using Go, I often encountered the map[string]interface{} type, which is really confusing for a new gopher. I used it for about a year without knowing what it exactly means.

Loading

@whoiswentz
Copy link

@whoiswentz whoiswentz commented Jul 23, 2019

I support this proposal too.

Its simple to implements and the readability becomes better

Loading

@Freeaqingme
Copy link

@Freeaqingme Freeaqingme commented Jul 24, 2019

I don't support this proposal. The power of Go is that for many things there's a single way of doing things. A language gets bloated easily once we start adding additional syntaxes only because some people prefer one over the other.

People new to the language may initially learn the 'any' keyword, but then read existing code, and still have to look up the interface{} keyword. Effectively, the burden of learning the language increases, rather than decreases.

Loading

@MaerF0x0
Copy link

@MaerF0x0 MaerF0x0 commented Jul 24, 2019

Yes and on w/ what @Freeaqingme has said, we also must take care when creating different ways of doing the same thing because:

  1. It makes tooling more difficult: now go tools have to support a new path to the same thing, and likely the users of said tools will expect output to match their chosen flavor of alias.

  2. It reduces the token space for future golang iterations. Consider alternate uses for the any token in future iterations of the language. We lose the option of clear expressiveness if we choose to alias any to something that is already provided. Some random ideas of how any could be something else: it could refer to any length of array ([any]string) , it could refer to non ZeroValue elements in a slice ( any mySlice) like lodash's compact function .

The examples are unimportant, but the point should be clear. If we choose to make this our any then we're stuck with it for quite some time, likely for good.

Loading

@dotaheor
Copy link

@dotaheor dotaheor commented Jul 24, 2019

Besides readability reason, less verbose and weird (esp. for new gophers) is another good point of this proposal.

@Freeaqingme
I began using Go by using Go templates to serve some web pages, I didn't need understand what interface{} is. I just needed to know I can assign any vale to it. any is far better for this purpose.

When a gopher later suddenly realizes that any is just interface{}, it is a viva moment, which in fact helps gophers understand Go better.

@MaerF0x0

It makes tooling more difficult:

any is just an alias of interface{}, there are many aliases in Go. If a tool can't handle such cases, it is a not qualified tool.

and likely the users of said tools will expect output to match their chosen flavor of alias.

This is not what a qualified tool should do.

It reduces the token space for future golang iterations.

Builtin identifiers are not keywords, users can shadow them as needed.

Loading

@Freeaqingme
Copy link

@Freeaqingme Freeaqingme commented Jul 25, 2019

@dotaheor

I just needed to know I can assign any vale to it. any is far better for this purpose.

If you really believe interface{} is so bad, I'd suggest you extend this proposal to deprecate interface{} and eventually remove it. I'm not a fan of that either, but my primary argument was against having two keywords for essentially the same thing. That argument would be moot if interface{} keyword is removed.

@MaerF0x0

We lose the option of clear expressiveness if we choose to alias any to something that is already provided.

We still have 'whatever' and 'anythingReally' as alternatives 😋

Loading

@changkun
Copy link
Contributor

@changkun changkun commented Jul 25, 2019

Is there anyone cloud explain the historical reason of why interface{} is designed as interface{} rather than anything else?

Loading

@dotaheor
Copy link

@dotaheor dotaheor commented Jul 25, 2019

@Freeaqingme
any will not be a keyword, it will be just a builtin identifier, just like, rune and byte, etc.

interface{} means an interface type specifying no methods, which happens to make it act as the any type. interface{} is not a bad thing generally, but using it as the any type is bad for new gophers. It confuses many new gophers.

Loading

@fzipp
Copy link
Contributor

@fzipp fzipp commented Jul 29, 2019

@Freeaqingme

If you really believe interface{} is so bad, I'd suggest you extend this proposal to deprecate interface{} and eventually remove it. I'm not a fan of that either, but my primary argument was against having two keywords for essentially the same thing. That argument would be moot if interface{} keyword is removed.

@changkun

Is there anyone cloud explain the historical reason of why interface{} is designed as interface{} rather than anything else?

It's not a special design, but a logical consequence of Go's type declaration syntax.

You can use anonymous interfaces with more than zero methods:

func f(a interface{Foo(); Bar()}) {
    a.Foo()
    a.Bar()
}

Analogous to how you can use anonymous structs anywhere a type is expected:

func f(a struct{Foo int; Bar string}) {
    fmt.Println(a.Foo)
    fmt.Println(a.Bar)
}

An empty interface just happens to match all types because all types have at least zero methods. Removing interface{} would mean removing all interface functionality from the language if you want to stay consistent / don't want to introduce a special case.

Loading

@Freeaqingme
Copy link

@Freeaqingme Freeaqingme commented Jul 29, 2019

@fzipp good point. I realized that after posting my earlier comment as well. Then it makes zero sense to remove that.

I'm sticking with my original reply; we can simply explain why interface{} is interface{} and how it works. There's then no need to add an additional keyword that basically is/does the same thing.

Loading

@sirkon
Copy link

@sirkon sirkon commented Jul 30, 2019

@Freeaqingme

If you really believe interface{} is so bad, I'd suggest you extend this proposal to deprecate interface{} and eventually remove it. I'm not a fan of that either, but my primary argument was against having two keywords for essentially the same thing. That argument would be moot if interface{} keyword is removed.

There was a built in type alias working like

type byte = uint8

in every Go package from the very early days.

It is not me being this proposal supporter or the other way, just a well known fact.

Loading

@y-usuzumi
Copy link

@y-usuzumi y-usuzumi commented Aug 1, 2019

In Haskell when we write guards:

abs n
  | n < 0     = -n
  | otherwise =  n

where

otherwise = True

Just imagine how much less readable it is if one uses True in place of otherwise.

It's only good if there's a single GOOD way of doing things.

Loading

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 27, 2019

If we are able to add generics to the language, then I suspect there will be many fewer cases where people write interface{}, so the alias would have correspondingly less value. Putting on hold pending a decision on generics.

Loading

@conilas
Copy link

@conilas conilas commented Sep 13, 2019

The any value acts like the top type in a type system and it may be usefull sometimes even with generics.

Say we have a higher order function that composes just for logs. You do not need a contract of anything like that for that type if you want to log the argument and then perform the action. Something like (take it as a pseudocode):

func add(a interface{}, b interface{}, action fn_type<args>){
  return fn(a, b) -> {
    fmt.Printf("%v %v", a, b)
    action(a, b)    
  }
}

The top type would be good in this case instead of writing interface{} either way, so I think this proposal still have some value.

Loading

@yiyus
Copy link

@yiyus yiyus commented Jul 26, 2020

Loading

@sfllaw
Copy link
Contributor

@sfllaw sfllaw commented Aug 22, 2020

Loading

@beoran
Copy link

@beoran beoran commented Sep 4, 2020

In the latest generics design, we find the following:

However, it‘s tedious to have to write interface{} every time you write a generic function that doesn’t impose constraints on its type parameters. So in this design we suggest a type constraint any that is equivalent to interface{}. This will be a predeclared name, implicitly declared in the universe block. It will not be valid to use any as anything other than a type constraint.

(Note: clearly we could make any generally available as an alias for interface{}, or as a new defined type defined as interface{}. However, we don't want this design draft, which is about generics, to lead to a possibly significant change to non-generic code. Adding any as a general purpose name for interface{} can and should be discussed separately).

I think the "It will not be valid to use any as anything other than a type constraint." is a bad idea. It is far more simple and consistent to allow the use of the any alias type any = interface{} everywhere, in the hope that as generics are introduced, we'll see it getting use less and less in non-generic code.

Loading

@billinghamj
Copy link

@billinghamj billinghamj commented Sep 9, 2020

I feel very strongly that any should either be allowed in both type parameters and everywhere else, or allowed nowhere at all. And quite strongly that it should be allowed.

Having a confusing-to-beginners pattern of using interface{} is negative

Having any which means the same thing but is only sometimes allowed will add to this confusion and thus is significantly worse

The only thing we must not do is make it more confusing. Adding any would be a nice improvement, but not conditionally

Loading

@quenbyako
Copy link

@quenbyako quenbyako commented Sep 23, 2020

I disagree too. Maybe in Dart, Typescript, Rust, etc. it can be good improvement, otherwise in go, you can just put in package simple file like
extra.go

package something

type any = interface{}
type null = struct{} // for channels f.e.

// something that you want to syntax sugaring

could be amazing practice! I mean, any/null is good, it's more readable, than interface{}/struct{}, but it can be implemented just now, in specific cases that you want.

Loading

@rsc
Copy link
Contributor

@rsc rsc commented Sep 20, 2021

Note that if you want to do the rewrite, gofmt -r 'interface{} -> any' works fine.

But gofmt shouldn't do the rewrite unconditionally, nor should check-in scripts, etc force use of any particular -r option.

Loading

@rsc
Copy link
Contributor

@rsc rsc commented Sep 22, 2021

We are going to accept this change. Robert will update the compiler.
Please do not send CLs updating the main Go tree to use any in new places.
We will do it in a single CL ourselves. Thank you.

Loading

@makhov
Copy link

@makhov makhov commented Sep 22, 2021

If I recall correctly there was a promise not to change the language in Go 1. And adding a new keyword is quite a big change.
There are a lot of packages, libraries, and apps that use any as a type, type alias, or variable name: https://github.com/search?l=Go&q=%22any%22&type=Code
Just assume the situation when your project stops compiling because of some indirect dependency that uses any.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Sep 22, 2021

@makhov: This isn't adding a new keyword, but rather adding a new builtin alias.

Any local declarations of any will take precedence over the builtin declaration for any. This would only break programs that were relying on the build failing if the any identifier was not present (which seems extremely unlikely).

Loading

@gopherbot
Copy link

@gopherbot gopherbot commented Sep 22, 2021

Change https://golang.org/cl/351456 mentions this issue: cmd/compile: allow any anywhere (as a type)

Loading

@makhov
Copy link

@makhov makhov commented Sep 22, 2021

@dsnet That's really good, thank you. But do you think it's a good idea to write something like this:

package main

import (
	"fmt"
)

func main() {
	var byte string
	byte = "foo"
	fmt.Println(byte)
}

How many people will be involved to support the change that brings nothing? We need changes in our tools, libraries, and applications.
For example, google.golang.org/grpc package uses any as a var name. So a huge amount of projects will be affected by the change.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Sep 22, 2021

Generally, it's not a good idea to shadow an identifier (builtin or otherwise), but it's not incorrect either. I don't think grpc (or any codebase) needs to change any of their usages of any.

Loading

@griesemer
Copy link
Contributor

@griesemer griesemer commented Sep 22, 2021

@makhov What @dsnet said. This is a 100% backward-compatible change. any is a predeclared alias for interface{}. Regarding your 2nd comment: You could always shoot yourself into the foot and write something like const true = false. The answer is simple: just don't do that.

Loading

@makhov
Copy link

@makhov makhov commented Sep 22, 2021

Generally, it's not a good idea to shadow an identifier (builtin or otherwise)

Right, but with this change, we turn a good code into an ugly one without any action from the author!

We all speak about simplicity, try to decrease cognitive load, do things in the most straightforward and obvious way, but here we introduce a small change that will make thousands of (or hundreds of thousands) people think and try to separate type alias from a variable reading already written code.

Loading

@fzipp
Copy link
Contributor

@fzipp fzipp commented Sep 22, 2021

Isn't the final comment period over?

Loading

@makhov
Copy link

@makhov makhov commented Sep 22, 2021

The answer is simple: just don't do that.

@griesemer and I don't. But now looks like someone is shooting in my foot without me doing anything :)

My point here is that the change will introduce more issues than value, at least now.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Sep 22, 2021

Right, but with this change, we turn a good code into an ugly one without any action from the author!

That's true of any language or library change, though. For example:

  • Adding unsafe.Add will make any occurrence of unsafe.Pointer(uintptr(ptr) + uintptr(len)) ugly.
  • Adding slices.Sort will make any occurrence of sort.Slice(x, y, func(i, j int) bool { return x[i] < y[i] }) ugly.
  • Adding bytes.Clone will make any occurrence of []byte(string(b)) ugly.

change will introduce more issues than value, at least now.

That's certainly subjective. The 118 thumbs up and the 44 thumbs down seems to suggest that most people believe this has value.

Loading

@makhov
Copy link

@makhov makhov commented Sep 22, 2021

That's true of any language or library change, though. For example:

That's not what I'm talking about, so I'd say your examples are irrelevant. I'm not arguing the transition from interface{} to any here (yet). Valid usage of var any is no longer valid after the change, that's the point.

That's certainly subjective. The 118 thumbs up and the 44 thumbs down seems to suggest that most people believe this has value.

I'm from Russia, don't tell me about democracy :)

Loading

@griesemer
Copy link
Contributor

@griesemer griesemer commented Sep 22, 2021

Valid use of var any ... remains valid after this change; that is the point of this change being backward-compatible.

Let's stop this discussion. This issue has been up for > 2 years and there was plenty of time to comment and plenty of discussion on its merits and disadvantages. Thanks.

Loading

@gopherbot gopherbot closed this in 1e57748 Sep 22, 2021
@rsc rsc moved this from Likely Accept to Accepted in Proposals Sep 22, 2021
@rsc
Copy link
Contributor

@rsc rsc commented Sep 22, 2021

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— rsc for the proposal review group

Loading

@rsc rsc changed the title proposal: spec: allow 'any' for 'interface{}' in non-constraint contexts spec: allow 'any' for 'interface{}' in non-constraint contexts Sep 22, 2021
@rsc rsc removed this from the Proposal milestone Sep 22, 2021
@rsc rsc added this to the Backlog milestone Sep 22, 2021
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 22, 2021

Reopening because this is not yet done.

@makhov This change is completely backward compatible. If you want to discuss why that is, please do so on golang-nuts, not here. Thanks.

Loading

@gopherbot
Copy link

@gopherbot gopherbot commented Sep 23, 2021

Change https://golang.org/cl/351731 mentions this issue: test/fixedbugs: adjust test case (fix longtest builders)

Loading

@makhov
Copy link

@makhov makhov commented Sep 23, 2021

Valid use of var any ... remains valid after this change; that is the point of this change being backward-compatible.

This change is completely backward compatible.

@ianlancetaylor @griesemer Only in the sense that it still will compile. But software engineering is much more than writing code that compiles, right?
Again, legit code will become ugly. Making the product of your customers worse is not a good way to go, IMHO.

Also, the issue has LanguageChange and Go2 labels. Will it be implemented only in Go2?

Loading

@griesemer
Copy link
Contributor

@griesemer griesemer commented Sep 23, 2021

@makhov To repeat what @ianlancetaylor already said: If you want to discuss why that is, please do so on golang-nuts, not here. Thanks.

Loading

@golang golang locked as too heated and limited conversation to collaborators Sep 23, 2021
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 23, 2021

Actually, this is done, sorry.

Loading

@rsc
Copy link
Contributor

@rsc rsc commented Dec 2, 2021

For people who are landing on this issue now, note that the reasoning for acceptance is in #33232 (comment), which is in the middle of the issue thread and therefore hidden by default.

Loading

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Proposals
Accepted
Linked pull requests

Successfully merging a pull request may close this issue.

None yet