9 ms·
An Introduction to Generics
- sudhirj 5y agoAn example of something that I couldn’t do with earlier non-generic versions of Go: implement the lodash functions in Go with a nice DX[1]. I kept using []T though, and the article has a good example of how to improve that to work with slice based type, not just slices. So many simple daily use methods like map, forEach, any, all, reduce etc feel much better now. On the whole generics here seem neat, well thought out, easy to understand and usable, while still allowing you to completely ignore them if you want. Most declarations fit neatly into libraries, with usage often never even having to mention or even know about type declarations (See the examples on the lib). That was a nice change from Java - the inference is pretty nice and neat from what I’ve seen so far. I’m sure we’ll find it being pushed to its limits, like with ORMs and multi chaining, and that might be ugly, but on the whole I’m very satisfied with this addition. [1]: https://GitHub.com/sudhirj/slicy https://GitHub.com/sudhirj/slicy
- deleted 5y ago[deleted]
- pkaye 5y agoThere is already an experimental slices package btw. It will be part of the standard packages once the finalize it. https://pkg.go.dev/golang.org/x/exp/slices https://pkg.go.dev/golang.org/x/exp/slices
- sudhirj 5y agoYeah, I’m using and delegating to it when possible. I don’t expect it to be as exhaustive as the lodash set though.
- morelisp 5y agoPlease don't slavishly copy Java/JS idioms. Go is not a language with a JIT and does not have a GC designed to handle lots of garbage; it assumes you won't allocate unless you need to. For example instead of func Drop[T any](array []T, n int) []T { if n > len(array) { n = len(array) } return array[n:] } Consider func Drop[T any](array []T, n int) []T { if n < len(array) { return array[n:] } return nil } Which can collect the underlying array sooner. Instead of your Chunk implementation (which, I want to offer, is better than most), consider an iterative zero-alloc version: func Chunk[T any](array []T, n int) (chunk, remaining []T) { return Take(array, n), Drop(array, n) } (And not performance-related, but instead of indices in `FindIndex` etc., consider returning pointers; they can then generalize more easily to non-slice sequences, or anonymous slices.) (And please don't call slices arrays.)
- sudhirj 5y agoThanks, Will keep this in mind when I do the perf edits and benchmarks. And yeah, didn’t want to confuse slices arrays, but it’s weirder to call them slices.
- jaytaylor 5y agotype Ordered interface { Integer|Float|~string } I kind of despise the keyword / syntax design choice of the tilde (~) signifying "anything where the underlying type is e.g. a string". Usually a ~ signifies a bitwise-NOT operation*. It reminds me of something more like a Ruby design mentality, where optimizing for code terseness is chosen. The nice thing about the crazy syntax in Ryby is that at least the chosen symbol is unique and doesn't conflict with meaning in other similar languages. I realize the context where it's used is not part of the logic-execution pipeline, but it still forces me to contort my mind and special case this concept, and map it against what is effectively a namespace collision between Go and other C-style languages. Naturally it's too late for Go (Generics are fully baked and released), but would have been nice to land with something more intuitive or at least less collision-prone, even if only to avoid the ambiguity. Inevitably, the introduction of generics means increased opportunities for new kinds of complexity, and this choice needlessly increases the cognitive load when trying to read and reason about a go program. I know it's the the end of the world, but I find it somewhat of a bummer for a technology I was previously so enthusiastic about. * edit: Thank you Jtsummers for the correction- it is bitwise-NOT, I had mistakenly written bitwise-OR.
- Jtsummers 5y ago> Usually a ~ signifies a bitwise-OR operation. That sounded wrong to me, I recalled it being a NOT, and double checked. Turns out my recollection was correct (it's also not something I ever used much), it is the bitwise-NOT in C. That actually makes it a bit more awkward, in my mind, as I initially parsed the example as "not string". However, ~ is also often used to mean "about" or "approximately". In a text exchange: "How many people will be at dinner?" "~5" Meaning "about 5 people" or "approximately 5 people". So this also works in that sense, ~string can be read as "something in the neighborhood of a string" or "something like a string". https://en.wikipedia.org/wiki/Bitwise_operations_in_C https://en.wikipedia.org/wiki/Bitwise_operations_in_C
- deleted 5y ago[deleted]
- altun 5y ago
- rank0 5y agoGenerics feel like OOP creeping back into popular programming culture. Everything just goes in a big circle. OOP is the bees knee's for like 20 years and then everyone decides that OOP is the devil for unnecessary abstraction. Then everyone gets all exited about functional programming, but they miss some of the convenience of abstract classes and inheritance. After developers make sufficient noise, we wind up with effectively the same OOP functionality *GASP. We just avoid words like "class".... EDIT: You guys are right about generics in FP. I guess my larger point is that lots language paradigms provide the same features just with different names and backlash is silly.
- yashap 5y agoGenerics are wildly, WILDLY popular in functional programming - far more so than in imperative flavours of OOP, really. Basically every functional data structure is full of things like List[A].map(func: A => B): List[B] Also, it’s really not “OOP or functional”, there are tonnes of languages mixing OOP and functional very effectively (Scala is both very functional and very object oriented, but honestly most languages are a mix to some degree). Functional vs. imperative is more of a divide.
- jaytaylor 5y agoAnd yet, FP remains less popular overall than OOP (personally, I love love FP :). Many of the people targeting the most popular FP language of all time aren't writing the Javascripts because it's their first choice. Just look at the pretty WILD popularity of Typescript, an OOP bolt-on language which transpiles to Javascript. The simpler (in some ways) imperative programming model still dominates developer mindset.
- vereis 5y agoTypeScript bringing a half decent type system to JavaScript is orthogonal to OOP JavaScript is OOP already. TypeScript just provides better typing like what you would find in Haskell, Ocaml or other functional languages
- eweise 5y agoNo type definitions for methods :(
- deleted 5y ago[deleted]
- pie_flavor 5y agoMethods exist to implement interfaces. Interfaces cannot have generic methods because those would need to be instantiated at runtime, which would require runtime codegen.
- sciolizer 5y agoWhy do you need runtime codegen? What exactly needs to be "instantiated"? Ultimately the runtime representation of a type parameter comes down to sizes and offsets. Why not have the caller pass those values into the generic method?
- smasher164 5y agoSee here: https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#No-parameterized-methods https://go.googlesource.com/proposal/+/refs/heads/master/des...
- hombre_fatal 5y agoNice find.
- adrusi 5y agoUgh yeah. The ability to check at runtime if a value satisfies an interface, combined with structural typing... So now the static expressivity of the type system is compromised in the name of runtime introspection. Reminiscent of how when Java added generics, runtime introspection ended up totally blind to them due to erasure. This seems like the result of not taking care to account for the possible future addition of generics when originally designing the language — it was always well understood that they’d be a likely later addition to the language. The ability to check if a value satisfies an interface at runtime doesn’t seem all that critical, although I could be missing something, I’m not a regular user of the language.
- klabb3 5y agoEveryone has strong opinions about language features. Personally, I'm happy that we had an imperative, statically typed mainstream language without generics for a long time. It really showed how much you can achieve without it in practical terms. Obviously, interfaces and built-in generic types like maps & slices, helped compensate a lot, but it's a quite simple feature set. I feel like I have a much clearer view today on when generics really shine compared to other alternatives. I hope, going forward, that responsible idioms around generics will be established in the Go community.
- fiddlerwoaroof 5y agoWe already had this language: the early versions of Java (1.4 and before) had no generics. Go always has annoyed me because the core language team comes across as repeating all the language design mistakes of early Java and other languages that treat their users like children.
- klabb3 5y agoWhat mistakes are you thinking of? To me Go and Java seems quite different as fas far as imperative languages go, but I don't know Java and it's history deeply.
- fiddlerwoaroof 5y agoThe old Java collection APIs were hampered by a lack of generics, so they tend to work in terms of the Object class (basically Java’s equivalent to Go’s interface{} except nominally rather than structurally typed). This means that the type system can’t encode and propagate constraints on the types of objects in collections, forcing casting and other things that lead to runtime errors. I’ve never personally seen a usable collections API in a statically-typed language without generics.
- skitter 5y agoAdditionally to the lack of generics, all references/pointers in Java/Go can be null/nil (so you can't express that a reference points to something) and all fields are zero-initialized (so having every field set to zero must be a valid state for your object). The former was - in reference to ALGOL W - called the billion dollar mistake.
- AmpsterMan 5y agoWe must be careful to not conflate our distaste of highly abstract OOP code with generics. Generics and OOP are orthogonal. If you've used a map or a slice in Go, you've used generics. If you've used a union in C, you (sort of) have used generics. I bet most complaints we hear about generics in Java, have little to do with generics themselves and mostly to do with inheritance, abstract classes, and so on.
- azth 5y agoMost "complaints" about generics in Java have to do with type erasure. It's a tradeoff at the end of the day, it opened up the JVM to host many high quality languages like Scala, Kotlin, Clojure, etc. Haskell also does type erasure as far as I'm aware. Furthermore, the JVM is now getting generic specialization for value types, so types like HashMap<int, Object> become viable without the cost of boxing. Inheritance based languages enable covariance and contravariance in generic types, something that comes in handy in many occasions.
- josephcsible 5y agoIf a language erases all types, like Haskell, it's fine. If a language doesn't erase any types, like C#, it's fine. If a language erases types in some places but not others, like Java, you get all sorts of awful problems.
- azth 5y agoCould you provide examples of such? I'm not aware that the Java team mentioned any when positing the primitive specialization design.
- valyagolev 5y ago“Haskell also does type erasure as far as I'm aware.” It’s not exactly an applicable term. Indeed Haskell doesn’t let runtime bother with a lot of information that it can provably dispense of. But there’s no laxity tradeoffs as in Java, Haskell won’t let you use a wrong type in a container
- khetnen 5y agoI really appreciated Go for its simple rules. It doesn't need these new features. You just surround the pieces.
- maleldil 5y agoYou aren't forced to use these features. You can ban in your own code and just reap the benefits of libraries written with generics. You were already benefiting from generic code in the language (maps, slices, channels, etc.) anyway.
- deleted 5y ago[deleted]
- deleted 5y ago[deleted]