In semantic versioning, going from 1.X to 2.X is only indicated when there are incompatible API changes. Adding generics doesn’t break compatibility with preexisting Go code, so it’s unnecessary to increment the major version.
Yes, it's not restricted to generics. But the reason the alias was introduced is generics. Because they use interfaces to specify bounds (constraints) for type parameters (bounded polymorphism): [T fmt.Stringer], [T io.Reader], ...
So an unbounded type parameter is [T interface{}], or [T any] when using the shorter alias. It's a type alias / predeclared identifier defined in the universe scope:
Disclaimer: I am totally newbie in Go. But quite experienced in Java and Js/Ts.
I never jumped the Go bandwagon because of the lack of generics.
Can I now try Go?
The following article seems to say that the lack of other features can be frustrating (the lack of lambdas and the lack of the functional handling of collections seems problematic to me)
Also I wonder whether the language has a IDE support as good as IntelliJ with Java (safe function extraction, type-safe autocomplete)
Would the local gurus here please comment?
[the idea for me is to replace my tricky shell scripts with Go scripts.]
If you're main problem with a new language is that it's not written like the language you are used to, you're not going to be happy with the new language.
Go has function literals, which are basically lambdas but a bit more verbose. Typical Go style doesn’t use them as often as other languages. You might assign one to a variable, giving you an inner function. But loops are usually written as for loops, not map calls. One API call that comes to mind where you might use one is ast.Inspect. [1]
Go often works well for replacing shell scripts, even without generics.
> I am [...] quite experienced in Java and Js/Ts.
> I never jumped the Go bandwagon because of the lack of generics.
> Can I now try Go?
Probably no. You still would be very disappointed. Go's take at writing and maintaining software often is pretty repugnant to people with a strong Java or JavaScript mindset. If missing user defined parametric polymorphism ("generics") was a reason to not even _try_ it you will be offended by by other things Go does in its particular way. Be it error handling, pattern matching, concurrency, mutability, etc. Basically if you try to write Java (or JavaScript) programs in Go you will suffer and hate Go. Same for C++/Rust aficionados. Go's newly added "generics" still come without "library support".
For IDE you can try goland(which is Intellij Idea for go).
For replacing shell scripts go should be fine. It's closer to C than Java, so you will often write "if err!=nil" which is not a problem actually. Go feels like C with Hashmaps and simplified threading.
Now that Go is going to have generics, all we need is sugar syntax for early return on error -- like more modern languages such as Rust and Zig have -- and Go may finally be pleasant to program in!
It's definitely more streamlined, but my concern would be that newcomers might not understand that it's just an alias. Learning Go really reframed my concept of what an interface is, and I thought that using an empty interface to represent "any type" was kind of ingenious, and helps reinforce its ethos.
An empty interface can represent any type because every type inherently implements an interface with no methods. And that's what Go is all about -- implicitly implementing interfaces.
If a newbie hops into Go and just starts using "any" I think they might assume it's a magic type that's at the base of everything, missing out on the fact that they're still taking advantage of interfaces.
Counterpoint: I'm an experienced Go dev but still think of "interface{}" as a semi-special "any" type, just because it fits my brain better conceptually. "Any type" is simpler than "an interface with an empty method set, which is of course matched by any type". I think this is a win.
> I thought that using an empty interface to represent "any type" was kind of ingenious
It makes me a bit sad to read that. We all are on a journey to be become better developers every day. But things like that are like a distraction. They make you think you found a really cool and smart concept, but you actually didn't and it's essentially just a hack.
Not sure what the solution can be. But PL designers should be more clear about features that are just "hacks" and considered "bad" but necessary in practice due to certain constraints. Go's {} and nil-errorhandling are examples. Nulls (in any language) are another common example.
I kind of understand what you mean, as it's similar to what happened with prototypes and the late-comer class syntax in JavaScript, and in that case, it was a net positive change as most would probably say.
I'm an experienced programmer whos just learning go now and I never made the connection that interface{} meant 'an interface with no methods'. I've always thought 'interface{}' was the clearest wart on the language. Languages shouldn't be about teaching me things, they are just a tool. Keyboard input goes in, computer instructions come out. any is easier to read, more intuitive in its meaning and takes less keyboard input.
> Learning Go really reframed my concept of what an interface is, and I thought that using an empty interface to represent "any type" was kind of ingenious
There's a few other languages out there that work that way. The split is probably is probably 20/80. Though "popular" languages heavily learn towards Java-esque interfaces - with TypeScript and Go being the odd ones in that bunch.
Emulating sum types in languages without pattern matching is extremely awkward, to the point of being almost useless.
type Result[T] struct {
ok: *T
err: error
}
Great, so how do you work with it?
* You can have a `ok()` getter that returns `*T`. Now you you need an `if x != nil`
* `isOk()` + `isErr()`
* `unwrap() T`, which panics on errors
* `split() (*T, err)` that splits into separate values, especially awkward since you both need `if err != nil` AND dereference the pointer
That API is more awkward then the status quo, and doesn't buy you any correctness guarantees because eventually you have to do an `if x := res.ok(); x != nil` or `x, err := res.split(); if err != nil {` anyway.
Pretty much the only convenience you gain are functions like `map()` or `unwrap_or`, but eventually you always have to take out the value and without being able to pattern match you can't get an API that improves correctness much.
A lot of people are going to write very un-idiomatic Go code with "Result" types built on simple generics that, 5 years from now, we'll all be kind of smirking at. Rust's Option and Result make sense because the language supports it (most importantly with match statements). They don't make sense here.
People downvote it, but it's true. 2/3 of your Go code is `if err` blocks. And the way you have to chain the error messages to make the stack make any kind of sense is just maddening.
I’ve been doing a lot of rust programming recently, but how exactly is the Result monad better? I feel like I end up with nested match statements for chained results, but maybe I’m doing something wrong.
Can you kindly post a snippet of what you want the early return on error syntax sugar to look like? I'm trying to map it in my mind on how I'd do it in C# and how it would look in Go.
Yes, please, the iferr pattern drives me nuts. While I built a few years of my career on golang, I'm absolutely sick of the language at this point. At least we finally get generics.
I would also like discriminated unions, and a more sane approach to code generation than go generate and writing your own binary that parses source and spits out source code.
> all we need is sugar syntax for early return on error -- like more modern languages such as Rust and Zig have -- and Go may finally be pleasant to program in!
Can you provide an example? What's wrong with `return err`? That's a very explicit early return on error, no magic or language features (= added complexity) needed.
I'm not sure I like this change. I liked interface{} since it just works out naturally from Go's relatively simple type system, and anyone could come to the conclusion without actually being told "use interface{} to represent any possible value" just by having an understanding of that type system. Adding what is simply a type alias, multiple words for the same underlying concept, to me just feels like jargon. I admire Go for its simplicity and clarity and this change gives me anxiety that that will be obfuscated.
I don’t think the concern is unreasonable at all but ‘any’ feels pretty solid as far as I’ve had the chance to poke around. There’s some tensions where I think ‘any’ is arguably much simpler:
The baseline is that constraints aren’t best expressed as interface literals in situ. Unlike exceptional use of ‘interface{}’, ‘any’ will be a more naturally invoked constraint.
Also, some of the uses of constraints rely on a unification involving an ‘any’ term that cancels out. Here, the use of ‘interface{}’ is not incorrect but maybe indirect.
with generics you wind up with two parameter lists, and type parameters have metatypes. All type parameters need to have a metatype, and the metatype `any` comes up a lot. Once you start using generics you'll start to feel the pain of writing `interface{}` in those signatures, especially when you are dealing with functions like this one:
I agree with you right now, but I wonder if I will change my mind after having had some experience using the new generics. Maybe it will feel more natural then?
Good, now we just need the ability to declare function parameters and return values non-nullable (Forbid passing nil into a function, and declare a function will never return nil).
That would get rid of the "panic: runtime error: invalid memory address or nil pointer dereference" errors.
… and pattern matching. Maybe just some extensions for `switch`.
… and one of the `try` proposals.
That having been said… I do appreciate that Go has gotten where it is today by being radically simple, and that a lot of extreme care needs to be done to add new features to the language. It’s hard to draw a firm line in the sand. I feel like all of these features would work great together, though; it’d enable Go to do something like `Result` in Rust with few language-level changes.
I even remain somewhat skeptical about generics, but I am hopeful.
I agree. I don't understand why the designers wouldn't want the language to be expressive. Wouldn't it be better to have an expressive language with conventions than one that's as rigid as it is today?
> … and pattern matching. Maybe just some extensions for `switch`.
Go has type switches which are… ok. If it were possible to “close up” interfaces and type switches took that in account (match completeness) you’d be done about done, you would not have the structural / patterned unpacking but that’s probably less of a concern.
> Good, now we just need the ability to declare function parameters and return values non-nullable
But that's already possible, just declare arguments and return values as values instead of references. I mean you'll get the zero value if you don't use those functions properly, but those won't cause nil pointer dereference errors.
I mean, it's a tradeoff between performance and developer competency. To be harsh, I think nil pointer dereferences are developer error, not a flaw in the language.
So now that go has generics and modules, is there an up-to-date intro for writing cutting-edge Go code for people who are already pretty familiar with the older styles?
I tried doing the modules tutorial a while back but had difficulty with the directory structure. At some point I think the "recommended" directory structure became mandatory and I don't think my personal dev environment ever quite lined-up with it. Perhaps I should wait another year then buy a book.
While it's good to get up-to-date, I don't think anyone should go re-write code or radically change their programming style to use generics. There's no need to over-complicate things. Anywhere that you're either copy-pasting a bunch, or using code generation might be a good fit for generics.
Edit: I’m impressed generics aren’t a breaking change! I thought this changed Interface{} to Any as a breaking change
For the release announcement: https://news.ycombinator.com/item?id=29556646
So an unbounded type parameter is [T interface{}], or [T any] when using the shorter alias. It's a type alias / predeclared identifier defined in the universe scope:
I never jumped the Go bandwagon because of the lack of generics. Can I now try Go?
The following article seems to say that the lack of other features can be frustrating (the lack of lambdas and the lack of the functional handling of collections seems problematic to me)
Also I wonder whether the language has a IDE support as good as IntelliJ with Java (safe function extraction, type-safe autocomplete)
Would the local gurus here please comment?
[the idea for me is to replace my tricky shell scripts with Go scripts.]
https://medium.com/webstep/a-java-developers-adventures-thro...
Go often works well for replacing shell scripts, even without generics.
[1] https://pkg.go.dev/go/ast#Inspect
Probably no. You still would be very disappointed. Go's take at writing and maintaining software often is pretty repugnant to people with a strong Java or JavaScript mindset. If missing user defined parametric polymorphism ("generics") was a reason to not even _try_ it you will be offended by by other things Go does in its particular way. Be it error handling, pattern matching, concurrency, mutability, etc. Basically if you try to write Java (or JavaScript) programs in Go you will suffer and hate Go. Same for C++/Rust aficionados. Go's newly added "generics" still come without "library support".
https://go.dev/ref/spec#Function_literals
Jet Brains makes GoLand, which is about as good as they come. It should feel familiar if you're used to IntelliJ. https://www.jetbrains.com/go/
Dead Comment
An empty interface can represent any type because every type inherently implements an interface with no methods. And that's what Go is all about -- implicitly implementing interfaces.
If a newbie hops into Go and just starts using "any" I think they might assume it's a magic type that's at the base of everything, missing out on the fact that they're still taking advantage of interfaces.
It makes me a bit sad to read that. We all are on a journey to be become better developers every day. But things like that are like a distraction. They make you think you found a really cool and smart concept, but you actually didn't and it's essentially just a hack.
Not sure what the solution can be. But PL designers should be more clear about features that are just "hacks" and considered "bad" but necessary in practice due to certain constraints. Go's {} and nil-errorhandling are examples. Nulls (in any language) are another common example.
Let's see how this plays out.
There's a few other languages out there that work that way. The split is probably is probably 20/80. Though "popular" languages heavily learn towards Java-esque interfaces - with TypeScript and Go being the odd ones in that bunch.
Having lots of syntactic sugar works out OK for Haskell.
For example, in Haskell straight-line imperative looking code is an alias for some underlying callback hell.
The state of error handling in Go at the moment is embarrassing at best.
type Result[T] struct { ok: *T err: error }
Great, so how do you work with it?
* You can have a `ok()` getter that returns `*T`. Now you you need an `if x != nil`
* `isOk()` + `isErr()`
* `unwrap() T`, which panics on errors
* `split() (*T, err)` that splits into separate values, especially awkward since you both need `if err != nil` AND dereference the pointer
That API is more awkward then the status quo, and doesn't buy you any correctness guarantees because eventually you have to do an `if x := res.ok(); x != nil` or `x, err := res.split(); if err != nil {` anyway.
Pretty much the only convenience you gain are functions like `map()` or `unwrap_or`, but eventually you always have to take out the value and without being able to pattern match you can't get an API that improves correctness much.
(std::optional in C++ is a great example)
People makes it a big deal, in reality it's not.
Can you provide an example? What's wrong with `return err`? That's a very explicit early return on error, no magic or language features (= added complexity) needed.
The baseline is that constraints aren’t best expressed as interface literals in situ. Unlike exceptional use of ‘interface{}’, ‘any’ will be a more naturally invoked constraint.
Also, some of the uses of constraints rely on a unification involving an ‘any’ term that cancels out. Here, the use of ‘interface{}’ is not incorrect but maybe indirect.
type Any interface{}
?
`type any interface{}` would declare a new type, while an alias is exactly another name for the same type.
Deleted Comment
That would get rid of the "panic: runtime error: invalid memory address or nil pointer dereference" errors.
https://wakatime.com/blog/48-go-desperately-needs-nil-safe-t...
… and pattern matching. Maybe just some extensions for `switch`.
… and one of the `try` proposals.
That having been said… I do appreciate that Go has gotten where it is today by being radically simple, and that a lot of extreme care needs to be done to add new features to the language. It’s hard to draw a firm line in the sand. I feel like all of these features would work great together, though; it’d enable Go to do something like `Result` in Rust with few language-level changes.
I even remain somewhat skeptical about generics, but I am hopeful.
> … and pattern matching. Maybe just some extensions for `switch`.
Go has type switches which are… ok. If it were possible to “close up” interfaces and type switches took that in account (match completeness) you’d be done about done, you would not have the structural / patterned unpacking but that’s probably less of a concern.
But that's already possible, just declare arguments and return values as values instead of references. I mean you'll get the zero value if you don't use those functions properly, but those won't cause nil pointer dereference errors.
I mean, it's a tradeoff between performance and developer competency. To be harsh, I think nil pointer dereferences are developer error, not a flaw in the language.
Deleted Comment
There is also a tutorial on generics: https://go.dev/doc/tutorial/generics
https://www.makeworld.space/2020/11/go_modules.html
Looking for a tutorial on generics myself.
The normie PL bar is rising, slowly but surely.
https://go-review.googlesource.com/c/go/+/368254
Is this a real command? If so, I’m very impressed. Is there any equivalent for c++ and other languages?
The combination of unambiguous syntax and consistently-formatted code results in rewrites producing meaningful diffs (most of the time).
[1]: https://pkg.go.dev/cmd/go#hdr-Update_packages_to_use_new_API...
[0] https://www.jetbrains.com/help/idea/structural-search-and-re...