13 ms·
Go 1.27 Interactive Tour
- lilbigdoot 2mo agoThis level of generics actually has me interested a bit in Go now.
- bilinguliar 2mo agoYou can take my place, as the same changes make me want to leave.
- melodyogonna 2mo agoTo what? What would you use instead, things like this seem common place in languages made in the last decade.
- bilinguliar 2mo agoZig. I know it has generics. But I am now more aligned with where the language is moving. I admire how easy it is to do data-oriented design in Zig. To put my money where my mouth is, I am donating $50 earned with Go to Zig every month.
- Hixon10 2mo agoSome examples for the upcoming release https://go.dev/doc/go1.27 https://go.dev/doc/go1.27
- chenxiaolong 2mo agoThis release also fixes runtime.findnull() to be compatible with MTE on Android ([1] and [2]). This was the only thing preventing MTE from being enabled for apps that use gomobile on MTE-compatible Android OS's like GrapheneOS. [1] https://go-review.googlesource.com/c/go/+/749062 https://go-review.googlesource.com/c/go/+/749062 [2] https://go-review.googlesource.com/c/go/+/751020 https://go-review.googlesource.com/c/go/+/751020
- fooooor 2mo agoI still don’t understand why Go isn’t the primary supported language for android.
- stingraycharles 2mo agoAm I the only one who’s absolutely shocked that Go finally is embracing generics? Does anyone have a bit of an inside view into what changed in the perspectives of the language maintainers? I’m not buying the “it took us 20 years to understand how to do it correctly” argument, as this is something you explicitly take into consideration when designing the language or not. And it was specifically not a part of language design, and is much harder to retrofit (backwards compatibility). So what changed?
- throw2ih020 2mo ago> what changed in the perspectives of the language maintainers? The original maintainers moved on to other projects and the new community maintainers came to a consensus through the proposal and governance process.
- bradfitz 2mo agoNo, that's not accurate. The same core people were involved.
- bradfitz 2mo ago(I was on the Go team for ages) Seriously, that's all it was. Just Ian alone proposed and rejected a half dozen of his own different approaches to generics. Finally a language + implementation plan came together that people all liked. Nobody was ever opposed to generics that I saw.
- lowmagnet 2mo agoThe implementation is coming along nicely, and I'm already thinking of ways of using generic bound functions. I'm not bad that it's not like Java 1.4 despite having the lessons of Java 1.4, but being class-based is a very different way of working from go. Getting the unbound functions right before the bound ones was a struggle, but I do think the approach makes sense, in the long run. I can't wait to see the Container stuff in the next iteration, too.
- 2mo ago
- nu2ycombinator 2mo agoThose Generics syntax in Golang seems so hard to read.
- theplumber 2mo agoIt is verbose but inference helps a lot to keep it “tidy”. I always find myself increasing my focus a notch when I start dealing with generics. It’s one of the things I use only if I really “need”.
- bessel-dysfunct 2mo agoYeah, I agree with that. Back when Go's generics came out, I was working with about 20% Go and 80% Python. I looked at the syntax, went "not today, Satan" and never bothered to learn it. For the past half year I've been in a mode where most of my coding time is spent with Go and I've been completely indoctrinated. I unironically like thinking about how generics and interfaces interact now. I also need to slow down when I need to use them for non-trivial stuff. Not just relative to Go code but relative to how much I needed to think about them back when I was using OCaml. I think part of it is that I save them for hard issues and use interfaces for easy stuff.
- xmprt 2mo agoOne of the reasons it took so long to implement generics in Go was because there was a lot of stuff you could do that didn't need it. Now that generics are there, a lot of that stuff is still the best way to solve the problem and many of the methods that require generics are in the standard library so it's rare that you absolutely need it.
- twsted 2mo agoimho it is much better that C++ equivalent
- Cthulhu_ 2mo agoIt does, but at the same time it's not "normal" code; I see it much like Typescript's advanced types, ultimately it's something that mainly lives in libraries.
- drivebyhooting 2mo agoCan generics be used to improve error handling and eliminate the if err pattern?
- jerf 2mo agoNo. I kind of want to leave it there. But that will probably be looked on disfavorably. I've seen at least a dozen attempts. It's not like it's hard to write it out. There's maybe a couple of variants but they're all just a handful of lines. The problem is, once you have an Option in hand, you end up trading: val, err := whatever(...) if err != nil { // handle error } // use val for val := whatever(...) if err, isErr := val.Error(); isErr { // handle error } realVal := val.Value() // use realVal What you win in nominal safety, you're definitely losing in convenience. There's also no win in trying to offer a monadic interface like finalVal := whatever(...).OnVal(func (val Value) opt.Option[Result] { // use val }) because that's the minimal specification of an anonymous function in Go, so it's very inconvenient. Even if that was trimmed down, nested functions are still problematic in other ways. And you still have to unpack finalVal anyhow. Really the solution is, install golangci-lint, turn on errcheck [1], use a pre-commit hook to make it a commit failure if golangci-lint fires, and that pretty much covers the problem in practice. One of the problems with Option/Result/etc. advocacy... not the pattern itself, the advocacy... is that it is generally are presented, implicitly or explicitly, as if the alternative is C, with its errno and the need to not just check an error value, but remember to go actively seeking out errors constantly, making it easy to forget. But by modern standards, that's completely pathological. If we rate error handling techniques on a scale from 1 to 10 (best), C here is a 1, and standard Option is maybe an 8 or a 9. The way Go does it is maybe a 6; it is completely true that you can neglect to handle an error (see errcheck comment in previous paragraph), but it is in your face that an error is possible, and that's really most of the problem. Putting Option/Result/etc. is not always a "go from 1 to 9" result. "Go from 6 to 8" is a much less impressive proposition, and the other inconveniences that come with it in Go tend to overwhelm the gain. I use errcheck all the time, and even in the Before Times when I was writing it all by hand it really didn't fire all that often. Especially if I exclude test code. In an AI era this hardly rates at all. AI never neglects the error. Whether it does the right thing with it, now... that's another story entirely. Read those error handling clauses if you're writing Go with AI. I really don't like what I've seen AIs do with them by default. What I've seen out of AI has been very thoughtless. Nominally correct in some weak sense, but thoughtless. [1]: https://golangci-lint.run/docs/linters/configuration/#errcheck https://golangci-lint.run/docs/linters/configuration/#errche...
- okzgn 2mo ago[dead]
- mappu 2mo agoAutomatically draining http response bodies is a risky silent behaviour change. I think it will be an improvement for most applications, but it's very subtle if you were relying on the old behaviour
- gigatexal 2mo agoCan you go more into this? I don’t quite follow
- mappu 2mo agoGo's http.Client will keepalive a TCP/TLS connection to save you handshake latency on second requests. But it can only do this if you completely finish reading the last request. Now in 1.27: > http.Response.Body drains itself on Close. For HTTP/1, closing the body now reads and discards any unread content (up to a conservative limit) so the connection can be reused. For most programs this is a transparent win [...] Great, so i no longer have to io.Copy(io.Discard, resp.Body) in the err case, one less thing to worry about; but > if you were leaning on an early Close to abort a large download, set Transport.DisableKeepAlives to opt out. That's a subtle behaviour change. Any previous Go program which used Close in this way - say for an infinite event stream - now hangs, soaking up bandwidth. In the past, the Go team have searched the entire Github corpus for misuse before making changes like this. I don't have a reference but I assume an appropriate level of consideration went into this decision. EDIT: ""up to a conservative limit"" so this is not so bad after all.
- spockz 2mo agoIs “a conservative limit” a high limit or a low limit? If it is high such that many responses will still be drained it would keep reading those infinite streams for a long time. If it is low it might still not drain all normal sized messages. Anyway, this is why it pays off to read release notes closely and have a decent test suite.
- mxey 2mo ago
- fang2hou 2mo ago[dead]
- theplumber 2mo agoThis is quite of a big release and I like the new methods on generics.
- Hendrikto 2mo agoMethods on generic types were already a thing. What is new is that methods can now define their own generic parameters, independent of the type they are defined on.
- mayama 2mo agoAdding simd in std and even being used in map is nice. Would have to look for places to experiment with it in hot loops in code I have.
- nothrows 2mo agogenerics were a slippery slope. give it a decade and Go will be indistinguishable from c++
- cookiengineer 2mo ago> generics were a slippery slope. give it a decade and Go will be indistinguishable from c++ Lib boost will have conquered every language by then!!! :D Jokes aside, generics are unusable in a lot of languages due to their syntax choices. In Go we kinda have the problem that there's no real templating and no real macros, so they're even harder to use. But I agree somewhat, generics feels to me like an anti pattern in Go. Also, the way the Go core/stdlib is written, it makes generics so unnecessarily painful to debug. Why they decided to have definitions like "~C" or "~[]S" is beyond me. No human knows what the resulting compile time error means. They should have named these things "Comparable" or "Slicable" or whatever is more expressive. Just stop with this stupid single letter shit.
- nirui 2mo agoNot here against Generic methods, but I feel the Go team is in a mid-age crisis where they lack of new things to do to prove themselves. See their iterators mini-drama not long ago? I feel the sumtype/emum/routine demanders should yell a little harder so Go team can find their purpose again.
- EdiX 2mo agoGenerics themselves maybe not. Generic methods probably yes. What people want generic methods for is to do deeply nested call chains that were never typical of Go. And if you have deeply nested calls you'll need some way to deal with errors in deeply nested calls, and then a short function syntax to pass to behavior inside those deeply nested calls. Give it a few years and everyone will be writing the same functional slop in Go that they are writing in every other language.
- foldr 2mo agoThe generic methods that have been added are essentially just syntax sugar. You can now use method syntax in cases where you could equivalently define a function. They’re not the fundamental extension to the type system that some people have been asking for (and probably will never get, because there’d be no reasonable way to implement it).
- sbstp 2mo agoGo's standard library has always been it's strength, especially the crypto package! Lovely stuff.
- kansm 2mo agoTried running a couple of examples in the tour, but ran into a few errors.
- deleted 2mo ago[deleted]
- wredcoll 2mo agoTried reading your comment but ran into a lack of usable information.
- wccrawford 2mo agoIf you try a new thing and run into 1 (or maybe 2 errors), maybe it's worth the time to report them. If you run into a few errors, it's into "this isn't ready" or "they didn't try hard enough" territory, and it's not worth reporting the problems.
- 63stack 2mo agoIt's also not worth posting "I tried didn't work" because nobody knows what you tried. There is zero information in it, and it's a waste of bits that just pollutes the discussion.
- wccrawford 2mo agoIt seems like that, but it's not. When trying to figure out a problem, even that tiny bit of information is worthwhile. If nobody else has posted it, it's the first indication that it doesn't work. If others have posted, it's an indication that it's a wider-spread problem than you knew. I'd still rather have more information, and I look down on people who only post something that useless. But it does have information I've used to figure problems out.
- pixxxel 2mo agoLet's GO
- cat-whisperer 2mo agodoes go have enums?
- adrianmsmith 2mo agoNo
- pkal 2mo agoOne reason that Go doesn't have sum/union types, from https://groups.google.com/g/golang-nuts/c/0bcyZaL3T8E/m/eL4r3VFKkR8J https://groups.google.com/g/golang-nuts/c/0bcyZaL3T8E/m/eL4r..., is that it is not apparent how this would mesh with Go's "meaningful zero value" stance.
- baalimago 2mo agoThis: "(b Box[T]) Map[U any](f func(T) U) Box[U]" is the type of cognitive weight I was happy that Go avoided.
- adrianmsmith 2mo agoI never understood the convention of using single letter names for generic parameters. I guess this started in C++ and every language has copied that convention. I think that code would be a lot easier to read if the types were called IN and OUT or In and Out or TIn and TOut or something like that.
- jiehong 2mo agoWe all know letters are expensive ^^
- phplovesong 2mo agoIm pretty sure it came from the MLs, where you usually have a/b/c instrad of the T,U etc combo. I dont find it confusing, as its pretty clear that it only an placeholder. In generics the name usually does not matter or is REALLY hard to name so that it makes sense. More specifically in Go where you have interfaces, concrete types and generics.
- asQuirreL 2mo agoFairly sure it would predate even that, and go all the way to lambda calculus, and predicate logic before that, and that's where my knowledge stops and somebody else can tell us where the current conventions around variables in logic and mathematics come from.
- teh64 2mo agoIn ocaml (and I assume SML) it helps that the generic types have a `'` before them, so val map : ('a Box) -> ('a -> 'b) -> 'b Box
- spockz 2mo agoCompletely agree and I personally name generic type parameters as I would name types and parameters. It helps a lot.
- vladsiu 2mo ago[dead]
- my-next-account 2mo ago>The quieter but bigger change I really wish they didn't use such stupid LLM-isms.
- neilprosser 2mo agoI do wonder whether, as a group of people being regularly exposed to text written by LLMs, we'll gradually end up writing and talking like that in our normal language. At that point perhaps text written by LLM and human will be indistinguishable. I already find myself using terms like 'footgun' in jest more than I ever did before! "The creatures outside looked from pig to man, and from man to pig, and from pig to man again; but already it was impossible to say which was which." ― George Orwell, Animal Farm
- yladiz 2mo agoThe percentage of people that use LLMs so much that their language would change based on its responses is small enough that I’d doubt it would happen. Maybe for technical groups that use it more, but not for the general population.
- zer00eyz 2mo ago> I do wonder whether, as a group of people being regularly exposed to text written by LLMs, we'll gradually end up writing and talking like that in our normal language. We were doing this before LLM's. All sorts of trendy business speak would spread - the term "synergy" springs to mind as one people beat to death. This is the concept of "memetics" (as in meme) in action. Hank Green recently talked about using AI and he went "off script" and threw "I appreciate the pushback" into his speech on the fly... LLM's generating large volumes of content means that we're going to see all of its "isms" creep into other peoples speech much faster.
- xboxnolifes 2mo agoThere's no doubt that people will. Even without LLMs people adopt writing styles from what they read.
- abtinf 2mo ago
- fweimer 2mo ago> interfaces still can’t declare type-parameterized methods What would an implementation look like? Wouldn't it be quite different from the existing one because it has to rely heavily on indirection because (limited) monomorphimization is not possible?
- cherryteastain 2mo agoProbably won't/cannot be ımplemented. C++ and Rust disallow the analogues of this (templated virtual methods/dyn with a trait containing methods taking type parameters) as well.
- deleted 2mo ago[deleted]
- deleted 2mo ago[deleted]
- smalljelly2018 2mo ago[flagged]
- adrianmsmith 2mo agoOne thing I think generics in Go is missing is the <?> concept in Java. If you're taking a List[T] and all you want to do is to call list.size() then you don't care what type of list it is. In Java you can write a function which takes a List<?> but in Go you have to write List[T] so then the question becomes what is T? You have to make the function (or type you're a method on) generic. If you make the type generic then every user of your type also needs to specify T, etc. I don't think it would be impossible to add that to Go. Allow List[?], which matches a List with any type parameter. Calling functions which don't involve the type parameter like list.size() would be fine, calling a method returning the type parameter like list.get(n) would return "any", and methods taking the type parameter like list.set(n, obj) would probably not be callable.
- Hendrikto 2mo agoGo Generics work differently than those in Java. They are specialized, meaning that they are not generic at runtime anymore. Instead, the compiler creates a different implementation for each type. At runtime, there are only List[int], List[string], etc. List[T] is not a thing anymore.
- lowmagnet 2mo agoThe one thing I hated about Java's generics was type erasure. It worked for the JVM but it had such stupid limits, like not being able to switch on a type, like go. (interfaces are types, this is exploitable)
- kbolino 2mo agoYou need this in Java because interfaces are explicitly implemented. You don't need this in Go because interfaces are structurally implemented. The way you spell "anything that has a method named Size which returns int" is interface{Size() int}.
- rednafi 2mo agoI am all for using LLMs to generate value but a little more editorial review can't hurt. > The quieter but bigger change: the classic encoding/json (v1) package is now backed by the v2 implementation under the hood. This is fantastic content nevertheless.
- Altern4tiveAcc 2mo ago>func (b Box[T]) Map[U any](f func(T) U) Box[U] {} That's completely unreadable.
- asdf88990 2mo agoYes, thinking in higher order abstractions is hard for most people.
- cpuguy83 2mo agoI'm pretty sure the issue is not "higher order abstractions". It is using multiple single letter references with no real grounding or relationship that the reader has to track. For example, looping over a map with "k" and "v" vars is not that bad because the reader understands k=key and v=value, and that makes since for a map. If you do this same thing with different single letter vars, e.g. "a" and "b", it instantly becomes more difficult to read. When writing a generic function and using these single character type references it can make sense, especially because the function/method doesn't care what those references are, however to someone trying to understand what's going on it can be extremely difficult simply because of the names. Sure, if all you are going to do is call that method or function those type references go away and the call site may be relatively clean, but you still have to read the thing to understand what it is and how to use it.
- fooooor 2mo agoOn the contrary, linq in C# is a killer function that other languages still fail to replicate. How many sloc are required for this? var max = mycollection.Max(); Probably 5-10 if you’re missing higher order functions. And the risk of bugs will be 10x.
- lowmagnet 2mo agoIt's showing you it's blending the `Box[T]` with a function that takes a function that applies over `T` to get a new type `Box[U]`. It's a bit convoluted, and it's also unnecessary. I think it's more readable like this: `func (b Box[In]) Map[Out any](f func(In) Out)Box[Out]` Or `func Apply[In, Out any](in []In, f func(In) Out) []Out` Which can be used anywhere and is not tied to a "Box" Honestly if it wasn't written in go it'd fit right in with Java, with a Verb in the kingdom of Nouns.
- KolmogorovComp 2mo agoInstead of having each language bring progress in a different and/or novel, we get this, old java features that comes 20 years-in after the making. They're probably useful, but clearly not sexy (as golang in general).
- YesThatTom2 2mo ago"The best way to teach something new is to compare it to something the audience already understands." Could someone take the example, reduce it to a non-generic version for two types I DO understand, then show that with the new feature I can collapse them into the Box/Map example in the doc? I have 10+ years of Go experience and I can't make heads or tails of "(b Box[T]) Map[U any](f func(T) U) Box[U]"
- deleted 2mo ago[deleted]
- LukeShu 2mo agotype IntBox struct { v int } type StrBox struct { v string } func (b IntBox) MapToStr(f func(int) string) StrBox { return StrBox{v: f(b.v)} } (Please forgive any typos I made on mobile.) It wasn't a great example because "Box" isn't really a useful type. But the point is that you no longer need to define a separate "MapToXXX" method for every type you might want to map to; now you can have just one type-generic "Map" method.
- majewsky 2mo ago> "Box" isn't really a useful type. It is very close to one. My Option[T] type [1] cannot have a Map method in stable Go because of the type system restriction in question. Instead, I have a separate package with freestanding functions with the same purpose, including Map [2]. [1] https://pkg.go.dev/go.xyrillian.de/gg/option#Option https://pkg.go.dev/go.xyrillian.de/gg/option#Option [2] https://pkg.go.dev/go.xyrillian.de/gg/options#Map https://pkg.go.dev/go.xyrillian.de/gg/options#Map
- typical182 2mo agoIt's not a great example. I think it's trying to show a mapping operation for a generic container where the container values are of one type and the mapping function is allowed to return a container with values of a different type. Without generics, something along the lines of the following (with runnable example at https://go.dev/play/p/KHBI1uAhbO0 https://go.dev/play/p/KHBI1uAhbO0): type MySlice []int // Map maps from a slice of ints to a slice of float64s. func (s MySlice) Map(f func(int) float64) []float64 { var out []float64 for i := range s { out = append(out, f(s[i])) } return out } From a quick search, this seems to be better explanation of this new 1.27 feature: https://www.gopherguides.com/articles/golang-generic-methods https://www.gopherguides.com/articles/golang-generic-methods (That uses an example that seems similar in spirit to the Interactive Tour's example, but with a more useful type of a Stack[T] and corresponding explanation seem clearer.)
- mangudai 2mo ago[flagged]
- ewy1 2mo agofascinating how many people are displeased with the expansion of generics! i love go and this is some functionality i always missed.
- neild 2mo agoMany comments on generic methods. Perhaps this example will help understand a hopefully not-too-objectionable case of using them in practice. The math/rand/v2 package has a number of functions which return random numbers of a certain type: i := rand.Int32() // a random signed 32-bit integer (type int32) j := rand.Uint64() // a random unsigned 64-bit integer (type uint64) It has functions which return a number within a range: in := rand.Int32N(10) // a random int32 in the range [0,10) jn := rand.Uint64N(100) // a random uint64 in the range [0,100) It also has a generic function, rand.N, where the return type is set by a type parameter. The definition of rand.Int32N (for comparison) and rand.N are: func Int32N(n int32) int32 func N[Int intType] (n Int) Int Adding some spaces to make the common elements align (apologies if my formatting gets mangled), that's: func Int32N (n int32) int32 func N [Int intType] (n Int ) Int As you can see, the generic function N has the same signature as the non-generic Int32N, except the type it operates on is set by a type parameter (named "Int"). The type parameter has a constraint, intType, which is a private type defined in the math/rand package. (There's nothing magic about this constraint, it's just a list of all the integer types in the language, and you can write it yourself if you want to. It's a separate type to keep the function signature of N from becoming too large, and it's internal to math/rand because it doesn't need to be part of the public package API.) The nice thing about rand.N is that it lets you write something like this: // d is a random time.Duration in the range [0, 10 minutes) d := rand.N(10 * time.Minute) Without generics, you'd instead write this as the following, which is a lot more noise: d := time.Duration(rand.Int64N(int64(10 * time.Minute))) The generic rand.N has a more confusing type signature and a lot more language complexity behind it, but the code using it is simpler and easier to read. We think that's a good tradeoff, but of course not everyone will agree. All the functions I've mentioned so far use a default random number source. Each of them also exists as a method of the rand.Rand type, which generates numbers from a user-provided randomness source. For example: rng := rand.New(rand.NewChaCha8(seed)) a := rng.Uint64() b := rng.Uint64N(100) There is one exception, though: Until Go 1.27, there was no Rand.N method, because we did not support generic methods. (A generic type could have methods, but those methods could not be further type parameterized.) In Go 1.27, there is now a Rand.N method: // Using a ChaCha8-based source with a defined seed, // generate a duration in the range [0, 10 minutes). rng := rand.New(rand.NewChaCha8(seed)) d := rng.Duration(10 * time.Minute) This method's signature is: func (r *Rand) N[Int intType](n Int) Int Comparing function vs. method and generic vs. concrete: func Int32N (n int32) int32 // function func N [Int intType] (n Int ) Int // generic function func (r *Rand) Int32N (n int32) int32 // method func (r *Rand) N [Int intType] (n Int) Int // generic method In this case, generic methods permit us to fix a small wart in the package API. This example isn't the motivating reason for adding generic methods, but I think it serves as an example of how adding them makes the language a bit simpler and more consistent. In Go, methods are just a type of function. Previously, you could write a type-parameterized function, but you couldn't write a type-parameterized method. That's an inconsistency that you need to remember. Now you can write type-parameterized functions or methods, using a consistent syntax for either. Type-parameterized methods don't participate in interface satisfaction, so this change isn't without its own subtleties. Discussing the tradeoffs there would double the length of this post, and weighing them is why it took so long for us to decide to add generic methods. Another possibility is that people will use generic methods to write unreadably complex code. My personal opinion is that nothing will stop people from writing unreadably complex code if they want to; the fix to complexity is to not do that.
- elsaicequeen 2mo ago[dead]
- freecodyx 2mo ago“Go 1.27 is a meaty release whose center of gravity is the type system. A few themes stand out:” llm generated
- lowmagnet 2mo ago> A quick credit first: the interactive Go tours were started by Anton Zhiyanov, who wrote one for every release from Go 1.22 through Go 1.26. He’s decided to stop, so we’re picking up where he left off. But he actually wrote those and they made sense.