golang/go

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

Lexkane opened this issue Β· 90 comments

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.

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

type any = interface{}

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.

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.

I support this proposal too.

Its simple to implements and the readability becomes better

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.

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.

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.

@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 πŸ˜‹

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

@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.

fzipp commented

@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.

@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.

@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.

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.

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.

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.

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.

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

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.

@ianlancetaylor

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.

From design proposal:

So in this design we suggest a type constraint any that is equivalent to interface{}

If I understand it correctly, it would result in having the "alias" ONLY for type parameter constrains (and nowhere else). IMO it would make the language not consistent.

I believe it would be wise to either rename any to interface{}, or simply deprecate interface{}, having two types that mean the same thing would put a lot confusion and ambiguity for new adopters of go in the future.

fzipp commented

I believe it would be wise to either rename any to interface{}, or simply deprecate interface{}, having two types that mean the same thing would put a lot confusion and ambiguity for new adopters of go in the future.

@g13013 They are not two types. One is the name (an identifier), the other is the actual thing. It's like being confused about fmt.Stringer and interface{String() string} existing at the same time.

@fzipp I think that actually any means interface{}, am I wrong ?

fzipp commented

@g13013 Yes, any is an identifier for interface{}, like fmt.Stringer is an identifier for interface{String() string}.

any is just an alias to interface{} it might be called an identifier, still it's the same thing, the point is, having two things that mean the same thing should be avoided.

fzipp commented

@g13013
Why do you not complain about having two ways to write fmt.Stringer?
You can write func f(s fmt.Stringer) {} or you can write func f(s interface{String() string}) {}

Why do you not complain about having two ways to write io.Writer?
You can write func f(w io.Writer) {} or you can write func f(w interface{Write(p []byte) (n int, err error)}) {}

Why is it suddenly a problem with any and interface{}?

@fzipp if so, how to use json.Unmarshal without interface{}/any?

any could be just alias) but i agree, that this problem could be solved without change language specification

@fzipp I would complain about func f(s interface{String() string}) {} and func f(interface{Write(p []byte) (n int, err error)}, we would definitely ban them in my team as it makes the code unnecessarily unreadable.

interface{} is an another story, its the de facto standard in go to accept any type, I think you got me wrong, if the go team choses to introduce any for generics, than the deprecation should happen in the documentation and guides not in the language itself, if we are willing to keep interface{}, then I would definitely prefer to write generics with interface{} instead of any.

re: #33232 (comment)

Now that the proposal is accepted (#33502 (comment)), what's next?

I believe the proposal here is to add a new predeclared identifier any that is an alias for interface{}.

I'll take this proposal off hold.

I strongly support use of "any" as an alias for interface{}, and I also support deprecation of interface{}. I think there may be an argument for having "any" and "anyref". The argument could be that "any" in a generic context has a different meaning and performance consideration than "any" in a non-generic context.

For example:

func M[T any](p1 T, p2 any) {
  // ...
}

The "p1" parameter is fundamentally different than the "p2" parameter. I can imagine some user confusion here.

That being said, I would like to discourage adopting two separate type names. I strongly support using "any" as both the generic constraint, and the alias for reflection-based polymorphism. Semantically "any" in both places means the same thing: a type must satisfy the constraint to be legally passed. There are differences in how the type looks to the user at compile time, but they aren't significant enough to warrant bifurcation.

I don't think this is a good idea.

Semantically speaking, interface{} means "interface that holds value, whose type guaranteed to have empty method set". Important thing there is that interface{} does not define type of value. It just wraps value of concrete type to use it in runtime.

I see that recent rise in discussion is probably because of approval of type parameters and its any type parameter. It is wrong to extend its usage to language, because in scope of type parameters any means concrete type rather than value wrapped into interface{}.

This change can be misleading since one may assume that any is concrete type while it is not.

@gudvinr in terms of go generics: interface{} == any, but any != interface{}. you can't set any value as any type, but you can set, for example (interface{})(nil)

in terms of go generics: interface{} == any, but any != interface{}. you can't set any value as any type, but you can set, for example (interface{})(nil)

It makes perfect sense because in terms of generics interface{} and any both aren't types but type constraints which later will be deducted into concrete type. In second statement interface{} is a valid type for value that holds another value.

@gudvinr absolutely not. In the world of golang, the interface{} is a type, same as fmt.Stringer, or error or even http.Server. The interface{} type itself is a pointer to anything that corresponds to the specified interface (f.e. error allows to store pointer to literally any value, that has at least Error() string method, interface{} allows to store pointer to literally any value that has at least be correct for go runtime). any is not a type as you correctly said, but it's constraint for specific types.

fzipp commented

I don't understand where the confusion or the desire to distinguish comes from.

These are interfaces:

any				// identifier
interface{}			// no identifier

fmt.Stringer			// identifier
interface{String() string}	// no identifier

error				// identifier
interface{Error() string}	// no identifier

You can use them as types:

var x any
var x interface{}

var x fmt.Stringer
var x interface{String() string}

var x error
var x interface{Error() string}

You can use them as constraints:

[T any]
[T interface{}]

[T fmt.Stringer]
[T interface{String() string}]

[T error]
[T interface{Error() string}]

An interface is not intrinsically a type or a constraint, it's just a method set. Whether an interface acts as a type or as a constraint depends on its usage position, not on its identifier or lack of identifier.

@nadiasvertex If you consider yourself "advanced user" you probably already know that usage of empty interfaces kinda discouraged.
In reality, most code written by inexperienced folks won't naturally contain interface{}. There are limited number of cases where you can't avoid them and it's basically either libraries for data serialization or some sort of containers and wrappers (like sync/atomic).

These kind of libraries usually require interface{} as input parameter(s). Even then it's being converted to reflect.Value right at the beginning most of the time.
In some cases type parameters reduce usage of interface{} even more.
And most importantly, this alias won't benefit existing libraries in any way because rewriting them using new alias is waste of time.

So why bother adding alias for sake of adding it? It has zero impact on existing code, it can't be widely used by "learners" and "advanced users" only use interface{} in write-once kind of libraries.
It isn't better in any meaningful way outside of discussions on how cool it would be.

@fzipp You should try to expand your example outside of definitions since they constructed in a way that only benefits your point but doesn't show difference in semantics while focusing on specific difference between having identifier or not.

In your last example, you only define constraints as type parameters. Code inside functions that use those parameters won't use these constraints explicitly.

While if you have interfaces as types you will probably use them a little more often than that. In that case, assigning something to variable of type any seems very wrong in statically typed language.

An interface is not intrinsically a type or a constraint, it's just a method set.

Interfaces are types that specify method sets. Not in some ephemeral sense, it is a struct that holds pointer and RTTI header.
And when you have some value with interface{} type you explicitly state that "value of that type will be wrapped in a struct".
It is well-known behaviour for advanced users and explained basically in every tutorial and article.

Alias for interface{} hides that subtle yet important meaning since any can easily be misinterpreted as "any type" which is not true. That means you must always keep this in mind this additional information.
For example, C++ users might assume that it works like auto and does some deduction at compile time.
If you have some previous experience with dynamically typed languages you might also wrongly assume that any has some properties that it doesn't have. Interfaces allow you to have type information in runtime but Go still statically typed language.

Constraints, on the other hand, are meta-types. You do not use constraint as a type inside parameterized function. In that case any literally means "any type" and after applying [T any] constraint, var value T will have value of type T, not type any.

@nadiasvertex If you consider yourself "advanced user" you probably already know that usage of empty interfaces kinda discouraged.

It doesn't matter if they are discouraged or not. They are the only mechanism that exists for accomplishing certain tasks. If you look through the go standard library you will see them used over and over. Whether or not a new user needs to (or should) "write" an interface{} taking function is immaterial since they certainly will need to use one.

For example, what is more immediately clear to a new user reading the following (real) prototype from the library:

func SliceStable(x interface{}, less func(i, j int) bool)

or

func SliceStable(x any, less func(i, j int) bool)

And when you have some value with interface{} type you explicitly state that "value of that type will be wrapped in a struct".

That's not true. You can turn integers into receivers with no more ceremony than a "type" statement. All function calls in go are statically bound at compile time. There is no implicit automatic runtime dispatch. While type assertions do invoke the reflection machinery, the asserted value is statically bound which (among other reasons) is why you have to check to make sure it worked before using it.

They are the only mechanism that exists for accomplishing certain tasks. If you look through the go standard library you will see them used over and over.

As you may notice, I said exact same thing after sentence you quoted. These usages limited by basically libraries for data serialization or some sort of containers and wrappers.

For example, what is more immediately clear to a new user reading the following (real) prototype from the library

Neither. You still need to explain what to pass in documentation. This case is also covered by "type parameters reduce usage of interface{}". So I don't get what's your point here.
And you still don't pass value of "any type", you pass wrapped value of concrete type so it's probably important to have this information right on hand.

And when you have some value with interface{} type you explicitly state that "value of that type will be wrapped in a struct".

That's not true.

What do you mean? This is struct that describes interfaces in Go runtime:

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type statement uses information from tab (which contains both interface type information and dynamic type information of stored type) to extract value from data. It is neither implicit nor automatic.

fzipp commented

@fzipp You should try to expand your example outside of definitions since they constructed in a way that only benefits your point but doesn't show difference in semantics while focusing on specific difference between having identifier or not.

In your last example, you only define constraints as type parameters. Code inside functions that use those parameters won't use these constraints explicitly.

@gudvinr Ok, here's an example with implementation:

func Concat1(a []fmt.Stringer) string {
	r := ""
	for _, s := range a {
		r += s.String()
	}
	return r
}

func Concat2[T fmt.Stringer](a []T) string {
	r := ""
	for _, s := range a {
		r += s.String()
	}
	return r
}

fmt.Stringer is an interface. In Concat1 it acts as a type, in Concat2 it acts as a type constraint. The difference in semantics between these two functions does not come from a difference in names (it's fmt.Stringer in both cases) but from a difference in usage position. So why should interface{} / any be an exception. Why artificially limit the scope of any as an identifier for interface{} to type parameter lists when this limitation does not exist for all the other interfaces. Implementation details like boxed in one case and not boxed in the other case (depends on how type parameters will be implemented) can't be the criterion as the fmt.Stringer example shows.

I think that all the debate stems from the overloading of interface with type constraints which I don't like.

Any is a constraint.
interface{} is a type that the overarching proposal wants to be allowed as a use to denote the same constraint.

The issue is that an interface has in general adhoc constraints itself (adhoc because the constraints are different depending on the compiler implementation of interface types)

Using interfaces as types as well asconstraints can be confusing at best.
Better way would be to have a notation to explicitly derive a constraint object from a type.

In our present case,

const Any = type constraint{
    interface{} 
} 

And even that needs to be looked into because the equality of constraints is not assured if we want to implement a constraint checker at the compiler level and not only user code.
In that case, we would need some type of constraint deriving function that would massage the type's constraint sets a bit.

Anyway, semantically, any should be different from interface{}.
It's more obvious when we look at interfaces and constraints in general.
Using constraints should not be boxing since constraints should not be types.
Using interfaces is boxing.
Having double semantics for interfaces is a bit unfortunate for future-proofing potential language changes.

[edit]
Cf. semantic subtyping for the warnings (the type systems are similar to Go but with variance.. We do not even have vatiance in Go which is actually great for constraint checking)

Also cf. intensional semantics where meaning is representation which is not the case for interfaces as constraints.

Cf. 2D semantics.

fzipp commented

I think that all the debate stems from the overloading of interface with type constraints which I don't like.

The fact that interfaces can also act as constraints is the whole genius and beauty of the type parameters design.

Unfortunately, I don't think it's manageable after having thought hard about it. It's confusing and the literature seems to warn against it.
I initially also thought that interfaces would be sufficient but the problem is that they are types.

fzipp commented

Unfortunately, I don't think it's manageable after having thought hard about it. It's confusing and the literature seems to warn against it.
I initially also thought that interfaces would be sufficient but the problem is that they are types.

The Featherweight Go paper has shown that interfaces as constraints work out. I'd be interested in the literature that you're referring to. I'm more worried about type lists in interfaces if they make some interfaces unusable as types. But I hear that they are working on an update to the design that addresses this.

Semantic subtyping is a variant of structural subtyping that has been implemented in an O-Caml based language.
They use a set theoretic interpretation of types.

They use a SMT solver to type check one of their language C-Duce if I remember well.

It's basically constraint type checking which is similar to having types as sets of propositions.

They even have the luxury of having variance bolted in the type system and make it still decidable if I still do remember well. Although I do not know how slow the type checking is because of it.

The warning is about a seemingly unrelated issue but that will apply here.
Can be found in "A gentle introduction to semantic subtyping" page 200. (available via a websearch)
This is about breaking the circularity problem in presence of arrow types (functions basically).
This is actually a similar issue to what I am hinting at.
Namely, the constraints on types are different from the constraints of types.
In other terms, do we have constraint sets first then types, or type-derived constraint sets?

Especially for interfaces.
And when solving, it needs to be taken into account for soundness.

They are the only mechanism that exists for accomplishing certain tasks. If you look through the go standard library you will see them used over and over.

As you may notice, I said exact same thing after sentence you quoted. These usages limited by basically libraries for data serialization or some sort of containers and wrappers.

Which means that new users especially need to understand them. Your argument that their use is limited and therefore not worthy of simplification does not hold up.

For example, what is more immediately clear to a new user reading the following (real) prototype from the library

Neither. You still need to explain what to pass in documentation. This case is also covered by "type parameters reduce usage of interface{}". So I don't get what's your point here.
And you still don't pass value of "any type", you pass wrapped value of concrete type so it's probably important to have this information right on hand.

The point is that inteface{} is a needlessly esoteric spelling of any type. We should just say "any".

And when you have some value with interface{} type you explicitly state that "value of that type will be wrapped in a struct".

That's not true.

What do you mean? This is struct that describes interfaces in Go runtime:

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

I think I have trouble understanding your English. Certainly if you pass a type into a function that takes a value interface what you say is true. My point is that just because something acts as a receiver doesn't mean that the value has to be wrapped up into yet another object. Also, what you point to is an implementation detail, not a semantic requirement of the language. The machinery of the implementation can be changed without necessarily affecting the semantics of the language.

rsc commented

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
β€” rsc for the proposal review group

Change https://golang.org/cl/347296 mentions this issue: all: gofmt -w -r 'interface{} -> any' src

I thought it was worth mentioning that interface{} has a natural pairing with struct{}, and both function very similarly to Any and Unit in Kotlin. Any is the equivalent of Java's Object, the automatic top of the class hierarchy, allowing any type to be assigned to a variable of type Any by way of subtype polymorphism, thus acting fairly similarly to interface{} in Go.

Similarly, Unit does the opposite, allowing only the value Unit to be assigned to it and thus carrying no information whatsoever, very similarly to struct{} and other, less commonly used empty types in Go.

Something like unit would have a quirk in Go, of course, as unit could only be a type, not also the value for that type, if it was just an alias for struct{}. The value would instead have to be unit{}, which is kind of odd.

rsc commented

@DeedleFake, unit would be a separate proposal (and exceedingly unlikely to be accepted). This is only about increasing the visibility of the now-already-existing any.

@rsc I know. I'm not proposing it. Just mentioning prior art and something generally related.

Personally, I'm not in favor of any, and one of the reasons for that is that unit, the natural counterpart, would be weird. If any hadn't been accepted for use in type constraints, I would have been very against its inclusion as a general-use alias, but because it has been, and only because it has been, it seems to make some sense. That doesn't sit well with me.

@DeedleFake, struct{} is not the counterpart to any. There are infinitely many possible unit types (imagine an unexported type unit struct{} in each of infinitely many packages), whereas there is only one any type: even if you define a separate any type in each of infinitely many packages, they are all interchangeable and mutually-assignable.

The type-theoretic counterpart to any is the β€œbottom” type (βŠ₯), which is the type of an expression that cannot evaluate to a value (e.g. one that must loop forever, panic, or call runtime.Goexit). There is no well-defined bottom type in Go's type system today, and I would be shocked if one were ever added, so I think its omission here is perfectly natural.

rsc commented

I posted https://golang.org/cl/347296 to show what a blind replacement would do in the main Go repo. (For backwards compatibility, we're unlikely to do much better than that.) Overall, the code is shorter and I think a bit clearer, but definitely feels foreign. That foreign feeling would dissipate pretty quickly. (I remember the same feeling when we started capitalizing for export and when os.Error became error. You adapt quickly, and the old code starts to look foreign instead.)

I don't believe the way the code looks is dispositive one way or another: it looks fine before, and it looks fine after. Instead I think we should be considering the larger effects of the change.

As I understand it, the main reason to decline this proposal, or put it back on hold, and leave 'any' only useful for constraints in Go 1.18, is to be cautious and move slowly. In general I think that's often good advice, but it's not a hard rule.

Here are some reasons for accepting the proposal in Go 1.18, which I personally find persuasive in aggregate:

  1. 'any' being only for constraints is a detail that will be in every writeup of generics - books, blog posts, and so on.
    If we think we are likely to allow it eventually, it makes sense to allow it from the start and avoid invalidating all that written material.

  2. 'any' being only for constraints is an unexpected cut-out that reduces generality and orthogonality of concepts.
    It's easy to say "let's just wait and see", but prescribing uses tends to create much more jagged features than full generality. We saw this with type aliases as well (and resisted almost all the proposed cut-outs, thankfully).

  3. If 'any' is allowed in generics but not non-generic code, then it might encourage people to overuse generics simply because 'any' is nicer to write than 'interface{}', when the decision about generics or not should really be made by considering other factors.

  4. If we allow 'any' for ordinary non-generic usage too, then seeing interface{} in code could serve as a kind of signal that the code predates generics and has not yet been reconsidered in the post-generics world.
    Some code using interface{} should use generics. Other code should continue to use interfaces.
    Rewriting it one way or another to remove the text 'interface{}' would give people a clear way to see what they'd updated and hadn't. (Of course, some code that might be better with generics must still use interface{} for backwards-compatibility reasons, but it can still be updated to confirm that the decision was considered and made.)

2. 'any' being only for constraints is an unexpected cut-out that reduces generality and orthogonality of concepts.

This also adds to the overhead of those people learning Go for the first time. Differences that feel like they should be the same thing often confuse people that are learning and not aware of the history of the language.

rsc commented

Based on the discussion above, this proposal seems like a likely accept.
β€” rsc for the proposal review group

There are infinitely many possible unit types (imagine an unexported type unit struct{} in each of infinitely many packages), whereas there is only one any type

@bcmills Is this because interfaces are a form of structural typing, while structs use nominative typing?

Just like byte is alias of uint8
And rune is alias of int32
Adding one more for readability is fine

Would gofmt rewrite interface{} to any automatically? I imagine this could be quite helpful in the pursuit of consistency and there being one way to do any given thing

Would gofmt rewrite interface{} to any automatically? I imagine this could be quite helpful in the pursuit of consistency and there being one way to do any given thing

I believe this would be a work better appropriate to go fix

Would gofmt rewrite interface{} to any automatically? I imagine this could be quite helpful in the pursuit of consistency and there being one way to do any given thing

I don't think gofmt should be rewriting interface{} to any. A programmer may choose interface{} deliberately, for instance because the intent is to add methods eventually. It would be really annoying if gofmt were to change that code. More generally, gofmt is in the business of organizing "white space", not rewriting code. (The fact that gofmt does have some options for rewriting, such as the -s and -r flags, is largely historic; we wouldn't be doing this now.)

rsc commented

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.

rsc commented

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.

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.

dsnet commented

@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).

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

@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.

dsnet commented

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.

@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.

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.

fzipp commented

Isn't the final comment period over?

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.

dsnet commented

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.

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 :)

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.

rsc commented

No change in consensus, so accepted. πŸŽ‰
This issue now tracks the work of implementing the proposal.
β€” rsc for the proposal review group

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.

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

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?

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

Actually, this is done, sorry.

rsc commented

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.

Change https://golang.org/cl/369954 mentions this issue: go/analysis/passes/stdmethods: recognize any as alias for interface{}, for errors.As check

Change https://golang.org/cl/382248 mentions this issue: compiler: accept "any" as an alias for "interface{}"