[HN Gopher] A Proposal for Adding Generics to Go
___________________________________________________________________
A Proposal for Adding Generics to Go
Author : jparise
Score : 185 points
Date : 2021-01-12 17:55 UTC (5 hours ago)
(HTM) web link (blog.golang.org)
(TXT) w3m dump (blog.golang.org)
| systemvoltage wrote:
| Side tracking a bit: I wish there was a popular programming
| language like Go with rust-like package manager, Python style
| syntax and ability to hack, compilable, classic (classes,
| methods), and fast. Or I wish Go had classic OOP and raise
| Exception methods.
|
| Basically, I want fast statically typed python with better
| package management. Or other way to put it, I want Go with
| classic OOP and Exceptions.
| switch007 wrote:
| This, kinda! But I'm after:
|
| - Python's OOP, stdlib, exceptions
|
| - Go's fast compilation and binaries
|
| - Rust's package manager (never used but I heard it's amazing)
|
| - Java's third-party packages & tooling
|
| - F#'s expressiveness, FP, type system (never used it, only
| read a few intro articles, but it looks very nice?)
|
| I'm tyring to move on from Python but finding it difficult to
| decide what to learn next.
| mseepgood wrote:
| > I'm after [...] never used but I heard it's amazing
|
| Really? This is how you form your opinions?
| switch007 wrote:
| Nope, I'm looking to _learn_ a new language, and I _heard_
| it's amazing. I didn't say it _was_ amazing
| Varriount wrote:
| You might be interested in Nim (https://nim-lang.org). I can't
| say that it's very object oriented, but it is very flexible.
| daypay wrote:
| Been using Nim for a few projects lately, absolutely love it.
| systemvoltage wrote:
| That's awesome, I will check it out. Just looking at initial
| syntax, you just made my day!
| azhenley wrote:
| How many different proposals for generics have there been?
| nappy-doo wrote:
| I think this makes the third official attempt. I have high
| visibility into the process, and I think it's likely this one
| will stick. It'll likely take 2 releases (as Ian stated) to get
| it done.
| EwanToo wrote:
| There's been quite a few early proposals and drafts. This is
| the first one that I'm aware of that Google have agreed to
| implement.
| spf13 wrote:
| This is the first. There have been several designs previously,
| each of which was refined based on feedback, culminating in
| this proposal.
| throw_m239339 wrote:
| Actually not that much, I remember 2 very different proposals,
| including this one.
|
| The previous one was confusing as F and the antithesis of the
| simplicity Go claims it abides by. It felt very much like a
| plot to add generics without ever using the word generics
| anywhere and looking too much like Java/C#/...
|
| The current proposal is basically what you'd expect from
| generics in a programming language, but a bit more limited.
|
| It took basically 10 years, a generation of developers, to
| quell the opposition against generics in Go, to end up with
| generics...
|
| They might even have unknowingly followed the ADA
| implementation except that Go's type inference makes them even
| easier to use.
|
| > To use a generic type, you must supply type arguments. This
| is called instantiation.
|
| https://go.googlesource.com/proposal/+/refs/heads/master/des...
|
| This is basically how generics as packages in ADA works. I
| would add that ADA solved many existing problems in Go decades
| ago...
|
| https://en.wikibooks.org/wiki/Ada_Programming/Generics
|
| > The generic procedure can be instantiated for all the needed
| types.
|
| Now all Go needs to do is to look at how Ada tasks work in
| order to fix every single issue with Go routines...
| wejick wrote:
| I'm curious what's the issue with go routine and what kind of
| fix ADA bring?
| boyter wrote:
| Not familiar with Ada but the inability to stop running
| goroutines from outside it is annoying and leads to a lot
| of state telling them to exit. Sometimes I just want it to
| stop now.
| rbranson wrote:
| How would you expect that to actually work though? If a
| goroutine could be arbitrarily stopped at any point, that
| would trivially lead to degenerate program states. A lock
| could be held open or another goroutine could be waiting
| on it for a message.
| wejick wrote:
| It's not like on pthread where we have a trhead handler,
| that will complicate many things.
|
| Like you said, We stop the go routine by telling it to
| exit the function, it's quite straightforward I think.
| Not more than a channel and switch case (+ context)
| dgb23 wrote:
| I have observed it from afar and my impression is that this is
| a close to final step in a long series of careful iteration,
| discussion and research.
| wejick wrote:
| It's very good to witness the journey of generic, how the team
| is very cautious and dedicated to bring Go way of perfection.
|
| IMO This is something that can't be done by a committee of so
| called industry players.
| ed25519FUUU wrote:
| I'm really mixed about this. As a developer I would _love_ having
| generics in Go. I can think of a few places in my code I can
| greatly simplify if they were implemented now.
|
| However, as someone who reads _other people 's_ Go code, I'm not
| a huge fan. One of the greatest things about Go is that a
| developer can usually one-shot read and understand almost
| anybody's code because there's a simplicity "forcing-function"
| applied to everything. To lose that would be a shame.
| __jem wrote:
| In other words, go is the language you want your coworkers to
| write, not the language you want to write. :)
| ed25519FUUU wrote:
| I agree totally with this sentiment. I love reading other
| people's Go code but not always writing it, which is
| basically the opposite of virtually every other programming
| language I've used.
| erik_seaberg wrote:
| I'm reminded of "languages designed for the masses":
| https://news.ycombinator.com/item?id=899246
| whateveracct wrote:
| parametric polymorphism does not make code harder to read
|
| you know what's hard to read? hand-rolled for loops & select
| blocks combined to wrangle concurrency.
|
| the no1 benefit of parametric polymorphism in Go is going to be
| abstracting over concurrency
| JamesSwift wrote:
| Exactly. I found myself so frustrated when learning Go and
| digging into the "gotchas" of goroutines. There is so much
| non-obvious complexity that could be completely avoided by
| providing generics so that someone else can develop a package
| to handle the issues for you.
| rowanseymour wrote:
| I rarely find myself frustrated with the lack of generics in Go
| and am so glad to never deal with the kind of over-engineered
| generic madness that is so common in Java, except...
|
| When dealing with collections. It's maddening to have to keep
| duplicating basic functions like getting the keys from a map, or
| checking if a slice contains a given item.
| gher-shyu3i wrote:
| > and am so glad to never deal with the kind of over-engineered
| generic madness that is so common in Java
|
| Over engineered how?
| wtetzner wrote:
| I don't think I've ever seen generics be the cause of over-
| engineered complexity in Java.
|
| It's pretty much always giant, complex class hierarchies or a
| bunch of reflection (or both).
| echelon wrote:
| Aren't collections 30% - 40% of code? (We seldom deal with just
| one thing.)
|
| That's why I feel generics are so important.
|
| You can build complicated messes with any programming paradigm.
| It's a matter of discipline to use the tool correctly. Don't
| hate on generics, but rather the unskilled use of them (which I
| frankly see far less than abuse of other
| patterns/paradigms/language features).
|
| The biggest negative with generics is compile time, but the
| clarity and conciseness of generics is worth it for me.
| dgb23 wrote:
| I find the proposal interesting. Type constraints might help to
| reason about a given abstraction. If I understand correctly they
| behave quasi like sum types over interfaces.
|
| From skimming here:
| https://go.googlesource.com/proposal/+/master/design/go2draf...
|
| They don't feel like generics in for example Java (my Java is
| rudimentary), but rather like an abstraction over interfaces.
|
| Can anyone elaborate on this?
| Animats wrote:
| Two more levels of blogs down, the actual proposal.[1]
| Definition: // Print has a type parameter T and
| has a single (non-type) // parameter s which is a slice
| of that type parameter. func Print[T any](s []T) { ... }
|
| Call: Print[int]([]int{1, 2, 3})
|
| Above, "any" is really just a synonym for "interface{}". You can
| have more restrictive type constraints on parameterized types by
| specifying other Go interfaces. This is vaguely similar to how
| Rust does it, and quite different from the C++ approach.
|
| _" This design does not support template metaprogramming or any
| other form of compile time programming."_
|
| [1]
| https://go.googlesource.com/proposal/+/refs/heads/master/des...
| fanf2 wrote:
| My understanding from the "Featherweight Go" paper
| https://arxiv.org/abs/2005.11710 is that generic types will
| _not_ simply be a synonym for interface{} because the compiler
| will be able to monomorphize them - they do not require dynamic
| dispatch like interfaces.
| oconnor663 wrote:
| I think the comment above meant that the `any` keyword
| specifically is a synonym for `interface{}`, not that all
| generic types will be.
| mseepgood wrote:
| > My understanding from the "Featherweight Go" paper
| https://arxiv.org/abs/2005.11710 is that generic types will
| not simply be a synonym for interface{}
|
| The `any` _constraint_ is a synonym for the `interface{}`
| _constraint_.
| lights0123 wrote:
| (also C++ concepts)
| alkonaut wrote:
| Finally. Hope it gets approved.
|
| It's strange that they don't consider Print[T](x T) instead of
| Print[T any](t T). The "any" could just be omitted without loss
| of anything. Especially since repeated types with the same
| constraints indeed DO omit it! Print2[T1, T2 any]
| setr wrote:
| well, it doesn't omit it; it just applies it to all preceding
| arguments that didn't have a qualifier, same as normal golang
| function arguments. It's still being explicit about the
| constraint being `any`
| djhaskin987 wrote:
| Would someone mind enlightening me as to what the difference is
| between this draft and the 2020 draft.
| ianlancetaylor wrote:
| Very little. The announcement here is not a new draft, it is
| starting the formal proposal process for adding type parameters
| to the language.
| betimsl wrote:
| > Interface types used as type constraints can have a list of
| predeclared types; only type arguments that match one of those
| types satisfy the constraint.
|
| Wait... But why?gif
| cratermoon wrote:
| I look forward to generics in Go. Yes, it's possible to do it
| with reflection, interfaces and interface{}, but it's not
| typesafe, it's not fast, and it's prone to code bloat.
|
| I'm a fairly late-comer to generics, I never programmed seriously
| in C++, I avoided generics in Java initially, and I wrote a lot
| of code in less statically-typed languages. Ever since the first
| serious talk of generics in Go 2.0 I've endeavored to educate
| myself and I now am very strongly in favor of them.
| moocowtruck wrote:
| generics in go will be a great addition, also it is important
| to realize generics in java and go are different such that go
| uses structural typing vs nominal
| cratermoon wrote:
| Yup, and they are different from generics/templates in C++ as
| well.
| majewsky wrote:
| I'm incredibly interested+ to see how this is going to affect
| kubernetes/client-go and friends.
|
| + Deliberate use of a neutral adjective.
| candiddevmike wrote:
| It's too bad this is targeting end of year, I have so many
| applications for this--test assertions, http controllers, SQL--
| this will remove a lot of duplicate code. I also think it will
| expand use cases for Go, especially in the UX area where you have
| to implement duplicative getters and setters.
| hackyhacky wrote:
| As much as I've cursed the lack of generics and the limited
| expressiveness of Go's type system, it's hard for me to reconcile
| these proposals with what I know of Go. Go was conceived as a
| small language, a successor to C, and purposely eschewed "new-
| fangled" features of modern languages. Whether the result is good
| is a matter of taste, but I feel that retroactively bolting on a
| modern type system will simultaneously (a) undercut the
| simplicity of the core language and (b) produce a language that
| is not as clean as those conceived with generics from the
| beginning.
| nine_k wrote:
| A successor to C with mandatory _garbage collection?_ Sorry, it
| 's a wrong ballpark.
| throwaway894345 wrote:
| Once upon a time, C was a general purpose programming
| language--it wasn't always exclusively performance critical
| systems programming. Anyway, in practice you can use arenas
| or other techniques to alleviate GC pressure. In my
| experience, the GC isn't a big performance issue; rather, the
| Go compiler doesn't optimize as aggressively as the C
| compiler.
| nine_k wrote:
| Unless you need the precise memory and hardware control
| that C gives you, why lower your productivity by using such
| a low-level language?
| dgb23 wrote:
| One could argue that parametric polymorphism _is_ a form of
| simplicity, because it disentangles algorithms from concrete
| types.
| kitkat_new wrote:
| The lack of generics makes it only more complex.
|
| Simplicity: yes, but in a senseful manner. Omitting generics is
| not senseful to me.
| hackyhacky wrote:
| I agree. But if you like generics, why not use a language
| that already has them, e.g. Rust, Haskell, etc.
| mrnothing_123 wrote:
| maybe you already use go-lang, and want generics.
| DaiPlusPlus wrote:
| Any successor to C needs to be type-safe. Generics make that
| easier.
| hackyhacky wrote:
| Why? C isn't type-safe.
| DaiPlusPlus wrote:
| C doesn't lack safety by-design - it's hobbled by its
| history and constraints imposed by its userbase - otherwise
| C would have major breaking changes more often.
| hackyhacky wrote:
| C absolutely lacks type-safety by design. Otherwise what
| would malloc return, besides void*? How would you
| implement generic containers in C, without using macros
| or void*?
|
| Edit: formatting
| DaiPlusPlus wrote:
| * malloc would accept a typename argument T and return T*
|
| * calloc would a;so accept a typename T and count and
| return T[count]*
|
| * Generic containers could be implemented as something
| closer to C++ templates (or preprocessor macros on
| steroids, like T4, as you suggest).
| hackyhacky wrote:
| There is no typename in C.
| peawee wrote:
| And that's (part of) why C needs to go.
| hackyhacky wrote:
| Sure. But why not replace it with something that is type-
| safe AND already has generics, like Rust? Why are we
| trying to get a do-over on Go?
| DaiPlusPlus wrote:
| At this point it's history: Go and Rust both got started
| around the same time - one by Mozilla and the other by
| Google. They've both reached critical-mass over the past
| ~10 years - so expecting one of them to disappear is like
| expecting Autodesk to choose between 3ds and Maya...
| seeekr wrote:
| Rust and Go both aim to create safe, performant, modern
| languages suitable for use from the systems layer up,
| differing in some of their primary goals:
|
| Rust is a sane C++: Zero-overhead abstractions, not
| afraid of language complexity.
|
| Go is a modern C: Simplicity, stability.
|
| They have some overlap in what they're best at, but they
| both take on unique and important missions that both need
| to be targeted, in our rapidly expanding universe of
| software engineering.
|
| (edit: formatting)
| williamdclt wrote:
| I think that's the point, the argument is that a "new"
| language needs (at least) type safety to successfully
| dethrone C
| esarbe wrote:
| I don't think that Go ever was a very good successor to C. Go
| might have been conceived as a systems language, but simply
| because of the GC it can never ever fill that niche.
|
| Go is a perfectly fine middleware language, but no C
| replacement. (Rust does much better in that regard).
| msie wrote:
| "If the proposal is accepted, our goal will be to have a
| complete, though perhaps not fully optimized, implementation for
| people to try by the end of the year, perhaps as part of the Go
| 1.18 betas."
| kitkat_new wrote:
| I hope at some point they manage to add it.
|
| I, however, discovered Rust in the meanwhile. It has generics.
| And it is not too complex either and has quite a few other
| bonuses.
| johnnycerberus wrote:
| I would say that in the case of Rust, generics are on the list
| of those "few other bonuses". There are other things in Rust
| that are more attractive and innovative. Rust is as complex as
| C++ (which is not a compliment) but saner and safer, without
| undefined behavior. Though, at the rate they add new features,
| I can see it becoming rapidly a kitchen sink.
| andolanra wrote:
| Rust is a reasonably complex language, but--having programmed
| professionally in both--I don't think it's _nearly_ as
| complex as C++. Rust features for the most part are
| orthogonal to each other, whereas C++ features tend to have
| weird interactions with each other that are really hard to
| track and understand. (The one place where Rust starts to get
| messier in terms of feature interactions is async Rust, but
| luckily you can program Rust just fine while ignoring the
| async features.)
|
| Consider initialization: C++ has _dozens_ of different ways
| of initializing objects that in turn interact in complicated
| ways with move semantics and references and so forth. Rust 's
| initialization story by contrast is straightforward: you just
| make the thing you want to make using struct or enum literals
| and maybe wrap those initializers in functions if you want.
| tick_tock_tick wrote:
| I agree I just have to assume people comparing Rust to c++
| never really used c++ professionally for any length of
| time. Rust is incredible simple coming from c++ there is
| normally only 2 or 3 ways to do something vs 5 + 4 more via
| templates. It takes longer to learn the symbols for
| lifetimes then to get a grasp of what you need to do to
| keep the borrow checker happy.
| echelon wrote:
| Rust is as complex as C++, but it hides features from use
| unless you know you need them.
|
| If you use vanilla Rust, it honestly feels quite high
| level, almost like Ruby or Python.
| tptacek wrote:
| I went from writing almost 100% Go to an environment where I
| write 60/40 Rust/Go.
|
| The worst thing we can have on any HN thread is a debate about
| the virtues of Rust vs. Go. They are different languages with
| somewhat different long-term goals and very definitely
| different short-term goals, and these threads are never
| interesting in anything but a sort of sporting event spectator
| way.
|
| I will just say that while there are a lot of things I like
| more about Rust than about Go, generics in Rust come at a
| cognitive cost. They're infectious; they don't get used the way
| people say they need them for Go ("I need to be able to sort
| arbitrary things and have sets of arbitrary types"); they're as
| fundamental to Rust as interfaces are to Go. It adds a lot of
| additional indirection.
| joshlemer wrote:
| Correct. As someone not doing Rust full time I do get tripped
| up on Rust's pervasive generics. Things like, "should I take
| a T argument here, or a U: Into<T>, or U: AsRef<T>" etc. Just
| seems like there's a lot of open world choices in the
| language which make it hard to get going quickly as a
| beginner
| schpaencoder wrote:
| How about the speed of compilation? Is it bearable?
| lights0123 wrote:
| It's very slow for the initial revision, as it has to compile
| all its dependencies. From there, if you add in full
| async/await support with a web server framework, you're
| looking at ~6sec iteration time. If you bring in LLVM's LLD
| instead of GNU or MSVC, you can bring that down to ~3-4sec.
| They're working on adding support for their own LLVM
| competitor, Cranelift, that should further reduce those
| times. It's only intended for debug/development builds,
| though, so you'll still need longer compile times for
| release.
| efaref wrote:
| While the compile times are a bit slower, one of the
| advantages of the strict type system is you don't need to
| compile to an executable quite as often.
|
| Most of the time your IDE's messages (or "cargo check"
| output) is sufficient to find all the things the compiler
| will complain about.
|
| I usually find that once I've fixed all of those, I compile
| it once and it just works.
| skrtskrt wrote:
| I like Go, but I too in the meantime have dipped my toes into
| Rust and it's just so much better without being _that_ much
| more complex. The learning curve is real but quite a bit
| overstated I think.
| throwaway894345 wrote:
| With Rust, sometimes I'm just breezing along wondering why
| people say Rust is hard. Then I try to deserialize some JSON
| into a type that borrows a reference...
|
| Also, Rust struck me as quite a lot more challenging before
| non-lexical lifetimes and rust-analyzer, so it's possible
| that you're responding to outdated criticism.
| egeozcan wrote:
| There's this common belief that "rust is too hard", which
| used to be actually true, but the docs and the language
| itself came a long way since those times.
|
| I'd say: If you can code in C#/TS (or anything like) + go,
| then it only depends if you have a free weekend.
| resonantjacket5 wrote:
| I mean the biggest change for most people with rust is
| really the borrow checker or aka the lack of a (automatic)
| garbage collector. I think it takes much longer to get used
| to it if one hasn't worked with say C or C++ before.
| EugeneOZ wrote:
| It really depends on what you are going to do with Rust.
| Sometimes it can be really tricky, but for simple tasks
| where we used to use scripting languages Rust can be
| similarly simple.
| dgb23 wrote:
| Rust has quite a few concepts you won't find in (some of)
| those languages like borrowing, lifetimes, traits,
| monomorphization, macros, type semantics around
| concurrency, (partial) expression based syntax, pattern
| matching and match guards...
|
| However you can litter your code with unwrap and clone to
| reach the finish line quickly, but then you lose the two
| main value props of the language and likely lose
| performance and runtime consistency over other languages.
| mikepurvis wrote:
| New concepts, yes, but I think anyone who has spent any
| significant time programming in C++ will immediately
| recognize the problems that they are solving and how the
| solution works. That significantly eases the learning
| curve in my opinion.
| recursive wrote:
| But we weren't talking about those who had spent
| significant time working in C++.
| mikepurvis wrote:
| Okay, fair. For myself, I'm coming from extensive
| experience in both Python and C++, and although I'm
| admittedly still in the honeymoon phase, my assessment so
| far is that Rust is an excellent union of the two.
|
| Basically I get the high level abstractions and package
| management that I expect from Python, while inheriting a
| set of tools that help finally realize some of the high
| performance, zero-copy idealism of C++ (slices,
| lifetimes).
| skrtskrt wrote:
| Indeed I am coming from Python (and some Go), and I'm
| coming to Rust because I want a language that makes me
| think about these things. But those who do not want to be
| bothered are likely to be turned off.
| oscargrouch wrote:
| C++ programmers are used to think in terms of object
| lifetimes, were they should be allocated, and how to
| signal ownership and object consumption over the API.
|
| That's why is better to learn Rust coming from a C++
| background.
|
| (And in my experience this mental model can also be of
| value even in languages were memory management is handled
| by the language runtime)
| cpascal wrote:
| I'm taking my second crack at learning Rust and I have to say
| I've made a lot more progress this second attempt. It could
| be just giving things time to stew in my head, but I really
| think it's because I'm using rust-analyzer with vscode and
| before I was using RLS. Rust-analyzer is a much richer
| experience and its informative error messages and suggestions
| lessens the learning-curve drastically.
| skrtskrt wrote:
| I am having the exact same experience.
|
| I think one think that could seriously be improved is high-
| quality explanations of Rc, RefCell, etc, and where and why
| they are used.
|
| I have found myself piecing together explanations from
| various books, Reddit threads, etc just to try to wrap
| around these.
| abhijat wrote:
| In my experience a combination of the rust book followed
| by the too many linked lists book was enough to give a
| pretty good idea of Rc, RefCell etc and how they can be
| used.
| jagger27 wrote:
| Here's the actual proposal:
|
| https://go.googlesource.com/proposal/+/refs/heads/master/des...
| cletus wrote:
| Another comment mentions this is the third (serious) attempt at
| adding generics to Go.
|
| Is there any concise history of these attempts (including this
| one)? I'd like to understand the gist of these proposals and what
| ultimately derailed them.
|
| By themselves these proposals are pretty inscurtable.
| cratermoon wrote:
| The golang nuts mailing list archive might be the best place to
| start. https://groups.google.com/g/golang-nuts
| philosopher1234 wrote:
| That is surely a history, but concise? Nope...
| cratermoon wrote:
| What I meant was, go ask there.
| tomlu wrote:
| The major difference is the first proposal separated interfaces
| from concepts, later proposals (very wisely) unified them. Only
| concepts could be used in type constraints.
|
| They also switched from parenthesis () to square brackets [],
| thankfully.
| tschellenbach wrote:
| We have a very large Go codebase here at Stream and not having
| generics is just not really as big of an issue as you think it
| is. There are plenty of work arounds if you get used to not
| having generics in the language. The fast compile times of Go are
| amazing. I was doing some Kotlin a few weeks ago and the
| difference is crazy. Go: Install deps, compile everything done in
| 5s. Doing the same in Kotlin, laptop freezes, android studio
| freeze, time to get a coffee :)
|
| That being said it would be really nice to have some reusable map
| type structures that handle GC better than the default maps.
| Fingers crossed.
| random5634 wrote:
| I'm pretty convinced they could have added some additional
| structure libraries to the standard library and called it good.
| Some map stuff / set stuff etc.
|
| For many its really not a big deal not having generics, and
| makes code SO much easier to follow (and compile / debug etc).
| egeozcan wrote:
| I actually just need generic Sets.
|
| Generic map/reduce on slices wouldn't hurt too.
|
| OTOH, it's 2021 and look at what we are wishing. My love/hate
| relationship with golang is like the one I have with Apple.
| jjtheblunt wrote:
| isn't generic Sets easily implemented with map being already
| generic?
| masklinn wrote:
| > isn't generic Sets easily implemented with map being
| already generic?
|
| Since Go has neither generic functions nor generic typedefs
| you can't implement a Set with a generic key type on top of
| map, you have to reimplement all the set operations for
| each key type you use.
| jjtheblunt wrote:
| I see what you mean there.
| coddle-hark wrote:
| I think map[T]bool is already a pretty good set; the only
| things you can do with sets are insertion, deletion,
| iteration and checking for existence and they're all
| well-supported.
|
| Of course, if you need a concurrent set you're right back
| in type system hell.
| erik_seaberg wrote:
| You can't write intersection, union, difference, subset
| (contains all), or powerset as reusable functions for any
| element type. The idiomatic thing is to rewrite them as
| loops over and over, which is error prone, hard to read,
| and not a good use of time.
| atombender wrote:
| One challenge there is that identity is only supported for
| some built-in types -- only primitives, and structs of
| primitives; no pointers, slices, maps.
|
| If you want a set of some complex kind of value that
| contains non-map-indexable types like slices and pointers,
| then you have build an indirection around it.
|
| A good set implementation needs to support a comparison
| operation. I really wish this existed for Go maps, too.
| fileeditview wrote:
| Yes and that's probably why there is no set in the go std
| lib. You just can use struct{}{} as (empty) value in a map.
| creata wrote:
| As an aside, this is exactly how HashSets are implemented
| in the Rust standard library.[0]
|
| [0]: https://rust-
| lang.github.io/hashbrown/hashbrown/hash_set/ind...
| joshlemer wrote:
| I thought the idomatic approach was to represent sets
| with map[key]bool
| fileeditview wrote:
| Hehe. It definitely was.. I think this is also still
| somewhere in "Efficient Go". However this seems to have
| changed in recent years. I was surprised by this too and
| personally I still prefer the bool even though it uses a
| bit more memory.
|
| People argue there are 3 states but it is meaningless in
| my opinion because you can just ask exists :=
| someMap[someKey] without checking for existence as you do
| with real maps. Here false is equivalent to not existent.
| philosopher1234 wrote:
| The bool is meaningless, so empty struct is more clear as
| it contains no state.
| jrockway wrote:
| A map[T]bool has 3 states for every key; absent, true,
| and false. A map[t]struct{} has 2 states for every key;
| absent, and present.
|
| People new to Go tend to pick map[T]bool or map[T]int
| because they're used to using bools and ints throughout
| their code, but struct{} is the correct value type for
| sets. (That is not to say that a counting set, map[T]int
| is useless, however. If you need that, use that!)
| majewsky wrote:
| I usually use map[T]bool because if _, ok
| := wasTouched[thing]; !ok { touch(thing)
| wasTouched[thing] = struct{}{} }
|
| is way uglier than if !wasTouched[thing]
| { touch(thing) wasTouched[thing] = true
| }
| masklinn wrote:
| The problem of doing that is twofold:
|
| * each key now has 3 possible states (true, false, and
| unset) rather than two
|
| * a bool takes 1 byte to store (which may get more
| problematic due to alignment, I've never checked what the
| memory layout of go's map is so I don't know how much of
| a concern it is there)
|
| An empty struct fixes these issues: a key being present
| means the item is in the set, and an empty struct is
| zero-sized.
|
| edit: apparently go maps are laid out as buckets of 8
| entries with all the keys followed by all the values, so
| there's no waste due to padding at least.
| RHSeeger wrote:
| As someone who finds "indicating intent in the code" an
| important thing, I must admit I find this concert
| slightly horrifying. A Map and a Set are two different
| things and which one you use conveys some intent as to
| what you mean by your code. I get that it works, but it
| would still make me unhappy to do.
| Jtsummers wrote:
| Ideally, if the language/standard library provides maps
| but not sets, and you wanted to use the idiomatic set =
| map of type -> bool approach, you'd create a wrapper so
| that intent is preserved but users don't have to know
| about the backing mechanism. Of course, it's obnoxious if
| everyone has to do this themselves _and_ the language
| lacks generics so you have to write this once for each
| potential type.
| philosopher1234 wrote:
| Wrappers that don't wrap very much aren't worthwhile IMO.
| Its like getting an amazon box with a fedex box inside.
| Just give me the package itself.
| Jtsummers wrote:
| Shallow wrappers that don't wrap much (now) but convey
| intent better are valuable if you buy into the idea of
| modularity and encapsulation in general. Some reasons
| (probably more out there):
|
| 1. The now-provided interface can more clearly express
| what the code is intending to do (better names for the
| operations you're providing than the underlying system
| has, _remove_from_end_ to _pop_ or _dequeue_ )
|
| 2. Hide methods of interacting with the underlying data
| structures that you don't want people to use (use a C++
| vector as a stack, but don't want random access)
|
| 3. You can replace the underlying mechanisms at will
| without impacting the users
|
| If you just wrap a vector in your own vector class and
| otherwise provide the same operations (or a limited set
| of operations but for no good reason to restrict usage),
| sure, that's moronic. But if you wrap a vector class in a
| "BigNumber" class and provide operations like _add_ ,
| _subtract_ , _mod_ , etc. then value has been added. Same
| thing with the idea of wrapping a map in a set interface.
| philosopher1234 wrote:
| Wrapping to hide is valuable, but wrapping has a cost
| which is generally underrated. Every wrapper is a thing
| itself which must also be understood when trying to
| understand how things work. And every wrapper is a
| division between blocks of code, meaning if you have
| changes which impact multiple layers of wrap, its harder
| to determine what to change, and to maintain the
| understandability of each layer.
|
| For this reason im an advocate of lazy wrapping. Create
| an abstraction at the last moment, when its painfully
| obvious what benefit it will provide, when you can see
| how it ties together disparate pre-existing code blocks,
| and when you have the highest confidence that it will
| stick and not need to be unwrapped next week by the
| senior dev.
| Jtsummers wrote:
| > Every wrapper is a thing itself which must also be
| understood when trying to understand how things work.
|
| I'd offer a different view. Wrapping/abstracting like
| this should reduce the amount of things a user of the
| abstraction needs to know. I don't care how Java's
| BigInteger class works under the hood, only that it does
| what I need it to do. If I did have to know how it worked
| to use it, this suggests a failure on the part of whoever
| created it.
|
| It _does_ increase what the maintainer of the underlying
| system (including the abstraction) needs to know, but if
| done in a sane manner this should not be a burden. So we
| 're making a tradeoff. The user gets something simpler,
| the underlying system maintainer gets something a bit
| more complex. Or the user gets something more complex and
| with more boilerplate but the underlying system
| maintainer gets something simpler (though will be
| pestered with, "Why don't you offer a generic set yet?"
| asked for years to come).
|
| > meaning if you have changes which impact multiple
| layers of wrap, its harder to determine what to change,
| and to maintain the understandability of each layer.
|
| When this happens, in my experience, it has meant one or
| more of:
|
| 1. The choice of how to wrap/abstract was poorly chosen
|
| 2. The choice was made too early (before the problem was
| properly understood)
|
| 3. A major change was made that would've been hard to
| identify/plan for earlier
|
| I ignore (3) when writing code beyond what's reasonable
| to plan for. (1) and (2) though mean I mostly agree with
| this:
|
| > Create an abstraction at the last moment
|
| But rephrased, borrowing the phrase I first saw in some
| Lean Software book, "last responsible moment." It's not
| sensible, for instance, to use a map to booleans as a set
| throughout the project's life and only wrap it at the
| last moment. If you know it's going to be a set, wrap it
| early because this offers clarity to your code and
| reduces boilerplate/noise. If you know you need a stack,
| and have a vector available, wrap it and hide the random
| access option. _If_ it later turns out that you _also_
| want random access, you can offer it, but if it 's been
| available from the start then users will have abused that
| and you won't be able to rein it in later (without a lot
| of effort and heartache).
| philosopher1234 wrote:
| I'm not sure how I feel about the `set` wrapper. I
| suppose its nice to hide some of the detail of how the
| set works. On the other hand, it is confidence inspiring
| to be told "this is just a map, its really that simple"
| as a user. I have a similar conflict about string alias
| types like `type MyId string`.
| coldtea wrote:
| Not if you need to implement set operations (union,
| intersect, etc.) on your own each time.
|
| Generic maps as sets are only ok for membership checks.
| shadowgovt wrote:
| > We have a very large Go codebase here at Stream
|
| That's exactly what generics are supposed to solve! ;)
|
| No, but seriously, I'll be interested to see if they can pull
| off maintaining the compiler performance while adding support
| for this new feature. I've had to hand-write a lot of code that
| I'm excited about a generics solution automating, but
| automation can have a price, you're exactly right. I've worked
| on a C++ codebase before that couldn't physically compile on my
| machine because it blew stack on template instantiation
| recursions (issue never noticed because the original developer
| had a better machine).
| ramoz wrote:
| In our large production project we have a very simple Go arch
| that enables 15+ microservices. Im not sure I see the value
| of generics either other than complicating a rather
| complicated distributed architecture with abstracted
| implementation.
|
| It's beautiful that we still import 6yo packages that are
| clear, concise, and work as needed. The package landscape
| with generics doesnt seem great.
| saturn_vk wrote:
| I don't think generics will suddenly make your, or anyone
| else's codebases more complicated. It might simplify code
| that's currently doing type assertions
| stickfigure wrote:
| I remember thinking something similar about my Java 1.4
| codebase back in the early 2000s. It's fine, right? What could
| I be missing?
|
| The answer is: A lot.
| praptak wrote:
| Go (without generics) is not Java 1.4.
|
| Go's built-in collections are actually generic and Java's
| generic-less awkwardness was mostly about collections.
| jacques_chester wrote:
| People from the post-1.4 era of Java aren't aware how _huge_
| code generation was before generics showed up.
|
| And then all that momentum, all the tools, the books, the
| conference presentations, the code ... all of it. Pop! It
| died. Because _code generation sucks_. It is the worst
| solution to any problem solvable by a type system.
| BoorishBears wrote:
| My favorite example of generics wonkiness was when I needed
| a channel to wrap an untyped channel to avoid "infecting"
| every call site for a utility function with untyped
| pointers.
|
| I thought it was madness, but bringing it up to a very
| large Golang group and get "nope channels are cheap! That's
| fine! There's repetition but it's easy to follow"
|
| I've said before, my personal take is use Go, get a feel
| for the Go mentality, then take it with you to another
| language.
|
| Go is just too stuck between low level and high level for
| me personally. I'd rather go under with Rust or over with
| Kotlin or C#
| sam_lowry_ wrote:
| I miss Java 1.4. It was small and concise. Java 5 added so
| much that none knows all of it. Just look at the length of
| Java Generics FAQ. It's hilarious.
| skinkestek wrote:
| I've
|
| - read that from start to end exactly zero times,
|
| - picked up generics in a day or two,
|
| - struggled with advanced types twice, half a day each time
|
| - struggled with type erasure twice, also about half a day
| each time.
|
| Meanwhile generics often saves me a number of minutes pr
| hour and makes everything cleaner and easier to read.
|
| I used Java before generics but once it arrived I never
| looked back and neither did anyone i know.
| karmakaze wrote:
| Java before 5 wasn't a language, it was a library and
| number of jvm implementations. It wasn't until Java 5 that
| there was a memory model spec that defined how stuff was
| supposed to work.
| uncledave wrote:
| Yeah that's ok until you have 20 million lines of generic
| ridden crapola pumped out by the lowest bidder. That's the
| hell I spent a good chunk of the last few years untangling on
| the C# front. Let's model this correctly! Oh no someone said
| fuck it, lets just use a bunch of generic data types!
|
| Dictionary<List<Dictionary<string, object>>>,
| SortedSet<Dictionary<int, string>>>>
|
| Several thousand out of bounds, missing keys, null reference
| exceptions, hash collisions and the hair starts to get thin
| on top. I'm not even sure I'm happy with it for abstract data
| types.
|
| Please can we keep Go special.
| travv0 wrote:
| > Several thousand out of bounds, missing keys, null
| reference exceptions, hash collisions and the hair starts
| to get thin on top.
|
| What does any of that have to do with generics?
| uncledave wrote:
| Well the generic programming model tends to favour using
| light weight abstract data structures instead of well
| defined types. Those abstractions are by nature leaky so
| many internal concerns leak out of abstraction boundaries
| into the caller and give them one hell of a bad time.
| Smaug123 wrote:
| `Dictionary<List<Dictionary<string, object>>>,
| SortedSet<Dictionary<int, string>>>>`
|
| This is not a problem with generics, but with C#'s lack of
| discriminated unions and/or tiny-types. Except what on
| _earth_ are you doing with a dictionary whose keys are
| lists of dictionaries? I am quite sure someone has not
| modelled their domain correctly there. That 's not
| something you can blame on the existence of generics - I
| shudder to imagine how much worse it could have been
| _without_ generics!
| uncledave wrote:
| I was exaggerating there I admit, mostly because I can't
| post some of the hell I've seen without breaking
| contracts. The worst example I've seen was a completely
| generic data type specified abstract syntax tree. I spent
| a couple of weeks rewriting that using concrete types and
| managed to find and fix tens of trivial bugs caused
| entirely by the design.
|
| The point is really that it's hard to reason about such
| things and define if they are appropriate or not for a
| lot of people. It's a lot of rope to hang yourself with.
| a1369209993 wrote:
| The sufficiently stupid-but-hard-working programmer can
| write crap code in any language. The actually-useful
| question is whether the language gives competent
| programmers enough rope to build whatever they're trying
| to build.
| uncledave wrote:
| We shouldn't build stuff for the 5%
| marcus_holmes wrote:
| Agreed. I'll be banning generics from any code I have
| control over unless there's a very good reason for it. I
| saw too much of this crap in C#, and ran from it screaming.
| strken wrote:
| When I worked at $big_company, we used a lot of code generation
| and reflection to work around the lack of Go generics in things
| like API interfaces to other services and test mocks. This was
| far from ideal, because it significantly increased compile
| times, and some things stopped being typesafe or had
| unfortunate type-related bugs.
| jeremyjh wrote:
| What is the workaround for having a nice ORM library?
| tschellenbach wrote:
| This one is mostly excellent: https://github.com/go-pg/pg
| konart wrote:
| Not using an ORM.
| dgb23 wrote:
| That's not a workaround, that's a solution.
| konart wrote:
| Every workaround is a solution, but not every solution
| was a workaround. :D
| jacques_chester wrote:
| If we're dealing in anecdata, mine is that "Go compiles fast!"
| is true right up until something in your dependency tree hauls
| in kubernetes repos, perhaps multiple times. Thanks the "first
| principles" design of go mod, that's becoming increasingly
| unavoidable.
|
| Kotlin does compile _much_ slower than I would like, but at
| least I only haul in one version of libraries and 0% of it is
| generated code. Java is basically instant for me on the same
| codebases.
| grey-area wrote:
| Don't do that then.
| dilap wrote:
| > Thanks the "first principles" design of go mod, that's
| becoming increasingly unavoidable.
|
| That's interesting, could you explain this more?
|
| Context: I used to use Go a lot, but mostly haven't since Go
| modules, and I'm curious to know the details of the problem
| and why it happens.
| jacques_chester wrote:
| It doubles down on Go's assumption that git repository ===
| a proper package/module system. It mixes up URLs and URNs.
|
| If your git repositories aren't tagged _just so_ , then go
| mod throws its hands up and simply invents a whacky
| snapshot version. Because it can't itself properly
| determine "earlier version" from "later version" on that
| snapshot, you often wind up with multiple snapshots from
| the same repo, not infrequently transmitted through other
| dependencies.
|
| This is just jolly good fun when it turns out that your
| dependencies are pulling in incompatible versions of
| things. Since the official Kubernetes policy for downstream
| consumers is "we don't care about downstream consumers", it
| happens more quickly than one would expect.
|
| As much as I have hated playing whack-a-mole with Maven or
| Bundler, I hate even more playing whack-an-adamantium-and-
| invisible-with-xray-eyes-mole against go mod.
| dilap wrote:
| Huh, interesting, thanks for the response.
|
| Does that mean Kubernetes is not following the version
| tagging policy in its repos? That seems...surprising!
| jacques_chester wrote:
| Kubernetes has its own tagging scheme, which makes
| perfect sense in its context.
| enneff wrote:
| > you often wind up with multiple snapshots from the same
| repo
|
| Either I'm misunderstanding you or you're mistaken. You
| can't have multiple versions of the same module in a go
| build.
| neetle wrote:
| _Technically_ you're right. Go considers v0/v1 to be a
| distinct module from v2. Most people would consider this
| to be the same module, but Go doesn't. If you want to
| know more, you can go read the manifesto released by the
| maintainers about how this is "the best thing ever".
|
| If you never tag v1, you'll never have to deal with it.
| dhagz wrote:
| I'm currently working on a project that depends on the k8s
| APIs and I haven't noticed ballooning compile times.
|
| I'm only pulling in k8s.io/api, k8s.io/apimachinery, and
| k8s.io/go-client though.
| jacques_chester wrote:
| My code before a transitive dependency pulled in the k/k
| universe took milliseconds to compile. Afterwards it took
| about 10 seconds to compile. Laboriously compiling
| thousands and thousands and thousands of lines of nearly-
| identical code turns out to be much slower. There are no
| clever shortcuts for a compiler that cannot deduce a higher
| intent.
| jrockway wrote:
| I have the same experience. I maintain lots of software
| that directly depends on k8s.io/go-client and it feels the
| same as any other Go project.
| johnnycerberus wrote:
| Java, Go, OCaml, Typescript (>=3.9) are in their own league
| when it comes to compilation speed.
| eeZah7Ux wrote:
| you mean slow?
| zenlot wrote:
| If anyone wants to check fast compile times try Delphi, RAD
| Studio has community edition which is amazing. It feels
| like it compiles while you type :)
| johnnycerberus wrote:
| I expect anything that has a little Pascal inside to be
| blazing fast.
| benibela wrote:
| And they have already added generics to Pascal
|
| It really makes Go look like a joke
| johnnycerberus wrote:
| The features I like about Go do not overlap with the ones
| of Pascal. But yes, it makes Go look underdeveloped in
| that department.
| lkramer wrote:
| This is not the first time I argue this case, but I would go a
| step further and say that not having generics is feature of Go.
|
| There are plenty of languages out there with Generics. I use
| several of them. I use Go when that suits me, and it's
| typically for cases where high readability trumps doing a lot
| of magic with generics. I think only once or twice have I
| thought to myself, "this would have been better with Generics".
| deskamess wrote:
| The introduction of generics would not change your workflow
| though. You could still happily "not use it" and keep matters
| readable. Others who wanted it, would use it.
| lkramer wrote:
| Mostly concerned about having to read other people's code
| that uses it.
| coldtea wrote:
| If others people code uses it, then those other people
| deemed it useful.
|
| So the argument for not having them now becames either:
|
| (a) they rather not have it available, because you
| personally don't find it useful
|
| (b) those using generic don't know what they're doing,
| and only people not using generic are smart, so it's
| better to not have them to prevent the clueless from
| being able to use them
| lkramer wrote:
| Like I said, I use (and like) several languages that have
| Generics, and when I need to do something where it makes
| sense, I can reach for those. For me there was an
| advantage in having a language where it wasn't an option.
| Your (b) scenario is quite a strawman. I have seen plenty
| of good code using Generics, but sure, there is subset of
| code written using Generics that is not good, and I think
| it becomes easier to obfuscate code and make it hard to
| read if you have Generics, that might just by my bias,
| and I think I'm tainted from C++ and hopefully it will
| never become as bad as what you can encounter there.
|
| I'm not trying to make out that Generics have no place in
| Computer Science. I was trying to make the case for it
| being nice that there was a language that didn't have it,
| and was building on the grandparent saying that he didn't
| miss it that often, which mirrors my experience with Go.
| philosopher1234 wrote:
| This is a silly strawman. Developers often write hard to
| read code, and even if generics are useful to the writer,
| doesn't mean they are useful to the reader. Many
| developers do not consider the reader, or if they do, not
| very in-depth. You can also make the argument that
| generic code is uniformly harder to read than specific
| code.
| wtetzner wrote:
| I would argue that code using generics is often _easier_
| to read than code without it.
|
| The fact that something is a type variable makes it clear
| the that type of that thing doesn't matter.
| jrockway wrote:
| That ship already sailed with widespread misuse of
| interfaces. Consider: package foo
| type T interface { func Bar() } func New() T
| { return
| &someotherpackage.ImplPickedAtRuntime{...} }
|
| Now whenever you see: x := foo.New()
| x.Bar()
|
| You have no idea where to read the code for Bar. For
| maximum fun, ImplPickedAtRuntime should then contain
| members that were allocated in the same way. What should
| be a simple M-. then eats up your entire afternoon.
| thomascgalvin wrote:
| Generics aren't magic, and they aren't Turing complete, like
| C++ templates. They're just a way to avoid copy-pasting code.
|
| Having `Set<Foo>` and `Set<Bar>` is far more readable than
| `class FooSet` and `class BarSet`, where the code is exactly
| the same aside from a search-and-replace.
|
| You also run into issues where someone finds a bug in
| `FooSet`, but doesn't know `BarSet` exists, and forgets to
| patch both. Now, you have two _divergent_ copy-paste classes.
|
| Generics solve a bunch of real-world problems, in a very
| simple manner.
| lkramer wrote:
| Can you give a real world example that couldn't be solved
| with interfaces?
|
| The only real cases I can see is creating new data
| structures, (for instance if you wanted to create your own
| map type).
| coldtea wrote:
| > _The only real cases I can see is creating new data
| structures_
|
| Half of programming is creating new data structures.
|
| The other half is tranformations (e.g. map, filter,
| reduce, min, max, etc) which also benefit from being
| generic.
| lkramer wrote:
| Sorry, when I said new data structures, I meant
| containers like maps and list, which I very rarely get to
| create day to day.
|
| I can see it for your transformations, but I have seldom
| seen cases where generics would really help (usually
| we're talking about comparing complex structure types
| that will need custom code anyway).
| Smaug123 wrote:
| "Theorems for free"! By which I mean, when I'm coding
| something and it _could_ be universally quantified on the
| type, then it 's better to do so. That way, it's
| impossible to phrase certain errors. For example, a
| function `Set<a> -> Set<a>` must produce a subset of the
| input set; that's guaranteed by the types. I can't
| accidentally include the value 0 in my set, because 0
| isn't of that generic type. The generic forces you to
| think in terms of the structure you're manipulating,
| rather than in terms of its contents.
| esarbe wrote:
| How about sets?
| Someone wrote:
| "Can you give a real world example that couldn't be
| solved with interfaces?"
|
| Of course not. Go is Turing complete, and generics do not
| make it Turing-completer (whatever that may mean)
|
| The question isn't about what can or cannot be done, but
| about expressiveness, ease of understanding for humans
| versus language size (even if you have plenty of disk and
| RAM, That correlates with buggyness of the compiler) and
| compilation speed.
| [deleted]
| candiddevmike wrote:
| I think you're looking at it wrong. You can absolutely
| solve it with interfaces, the problem is those interface
| methods are identical, so it's duplicative.
| masklinn wrote:
| > I think you're looking at it wrong. You can absolutely
| solve it with interfaces
|
| There are lots of generics use case you either can't
| solve at all with interfaces, or you have to contort
| every which way and usually lose something in the process
| (type-safety, performances, readability, ...).
| chowells wrote:
| You can't use interfaces to prevent inserting values of
| the wrong types into a set. When you view the purpose of
| types as preventing bugs, that seems like a giant missing
| feature.
| randomdata wrote:
| The biggest challenge is avoiding the tricks from languages
| that allow you to use the language to paper over questionable
| design choices. Go requires that you get the design right up
| front and provides few escape hatches to save you if you mess
| up. Which is a good thing, but makes the language (not just the
| syntax) difficult to learn compared to others.
| TheDong wrote:
| Can you give examples of specific language features of go
| that prevent you from making questionable design choices,
| that require you to "get the design right up front"?
|
| When I hear that, the first things that come to mind are
| things like haskell's IO monad, which forces you to model IO
| better than go or most other languages, or haskell's other
| state monads which similarly force you to model state more
| explicitly.
|
| I think of rust's lifetime and ownership system, which forces
| you to correctly model the ownership of types and prevents
| quite a few bad design patterns (which I see constantly in go
| btw; the number of times I've seen races due to multiple
| goroutines writing to a struct is large, the number of times
| I've seen incorrect synchronization that rust would have
| prevented is large).
|
| I can't think of anything in go that pushes you towards
| designing your code well in go, especially when compared to
| languages with more complete type-systems.
| randomdata wrote:
| _> Can you give examples of specific language features of
| go that prevent you from making questionable design
| choices, that require you to "get the design right up
| front"?_
|
| No, obviously. I said that Go _doesn 't_ give you an escape
| hatch _if_ you screw up. It does nothing to protect you
| from screwing up. I specifically said that the challenge
| was in learning how to not screw up as the language doesn
| 't help you deal with or avoid design mistakes.
| TheDong wrote:
| Aaah, I read "Go requires that you get the design right
| up front" totally wrong, as in "go prevents you from
| getting the design wrong", not "go lets you get the
| design wrong, and then doesn't help you".
|
| I still don't get your contrast to other languages
| though. Are there specific language features other
| languages have that let you paper over crappy designs?
| enneff wrote:
| IMO Go's escape hatch for screw ups is its superb refactor-
| ability.
| kitd wrote:
| Generics make working with reactive APIs much easier, which I
| think fits with a large chunk of Go's target audience (b/e
| networked services) quite closely.
|
| Writing APIs to deal with futures, etc, is much easier when you
| can chain parameterised functions together.
| johnnycerberus wrote:
| Measuring against Kotlin or other languages in this category
| like Scala is not the right thing to do. Also Kotlin is known
| for its slow compiler though JetBrains promised that the
| situation will improve. I would use Scala or Kotlin for
| everything data but when it comes to low-level
| networking/infrastructure I wouldn't even touch anything else
| but Go. Go is in the perfect sweet spot for this task. The
| Goldilocks Zone of network programming.
| BoorishBears wrote:
| It's such a truly terrible comparison because they didn't
| compare Kotlin, they compared Android!
|
| The insane amount of time of overhead an Android project has
| over a "plain Kotlin" from resource packing to dex stuff to
| desugaring to ProGuard, it made me question if they're
| speaking in good faith for the rest of the comment...
|
| My ktor projects on the other hand compile incredibly
| quickly, and with hot reloading it's even more seamless to
| iterate
| brundolf wrote:
| It seems to me like generics are extremely important for
| "library code", and not super important for "application code"
| (and in fact they can sometimes create more confusion than
| they're worth in the latter context). Go also seems like a
| language that thrives in smaller-scale, application-focused
| contexts (microservices being the obvious example).
|
| So in this light, and with the basic generic data structures
| supplied by the standard library, it seems to make sense for
| "user-level" Go code to generally be better-off without
| generics
|
| Of course the line between "library" and "application" code
| isn't well-defined (especially if you consider libraries
| outside of the standard one), which is probably where most of
| the pain-points are coming in
| enneff wrote:
| I generally agree but I note that any substantial project
| becomes largely composed of "library code" itself.
| brundolf wrote:
| Right, and that's where the question gets muddy
|
| Though again, Go as a whole seems ill-suited for scaling to
| larger projects because of lots of other limitations on its
| type system, reliance on conventions, implicit-defaults,
| etc. Which makes it well-suited to (and often used for)
| things like microservices, where each actual codebase is
| smallish. Codebases like these will tend towards having
| less "library-like" code anyway, which means they don't
| need generics as badly. There's synergy here in the
| language design.
|
| So I guess what I'm saying is: leaving out generics seems
| like the more "Go-like" direction, will dovetail better
| with its overall philosophy, etc, and isn't without
| advantages. But it would also mean kneecapping the language
| when it comes to certain use-cases that it's never going to
| be great for anyway. It's the classic "opinionated" vs
| "everything for everybody" dichotomy
| enneff wrote:
| Funny you should say that, because go was specifically
| designed for large code bases. I work on a large go code
| base and it's great.
|
| https://talks.golang.org/2012/splash.article
| philosopher1234 wrote:
| Why do you say Go has trouble scaling to large code
| bases? Is that something you'd expect, or something borne
| out by the evidence? And if so, what is the evidence?
| brundolf wrote:
| It's the subjective impression I've formed from, among
| other things, reading articles like this:
|
| https://fasterthanli.me/articles/aiming-for-correctness-
| with...
|
| Ctrl+F for "Let's start with Go" to jump to the relevant
| part
| philosopher1234 wrote:
| FWIW I would take fasterthanli.me with a grain of salt.
| The guy is a serial Go hater. His points stand on their
| own, but I don't think he appreciates Go's benefits. I
| think "A Philosophy of Software Design," Rob Pike's
| talks, or Russ Cox's blog posts are a good place to look
| if you want to understand what is valuable about Go and
| the reason to believe it would actually scale very well
| to large codebases.
| brundolf wrote:
| Thanks for the references, I'll look at what the other
| side has to say
|
| I am aware that fasterthanli.me can be a bit, shall we
| say... opinionated. Though as you say, his points do
| stand on their own. I can see the things he points out
| about the design philosophy of Go's language features and
| standard library and draw parallels to languages and
| libraries that I've used firsthand, and had firsthand
| frustrating experiences with when it came to navigating
| their magical behavior and lack of enforcement of
| contracts. But I'll keep an open-mind
| philosopher1234 wrote:
| Certainly. As a counter-example to the pain of implicit
| functionality, take the UNIX file API. `open(1),
| write(1), close(2)...` represent hundreds of thousands of
| lines of code, spanning network devices, local file
| storage, integrity checking, and who knows what else, and
| all of it is hidden. It is precisely the mountainous heap
| of implicit behavior that gives these APIs value.
|
| That being said, APIs with implicit function that are
| broken or surprising are painful, but I take this not as
| an indictment of implicit function, but as an indictment
| of buggy APIs. I think state and hidden functionality is
| the essential ingredient of highly useful code.
| mumblemumble wrote:
| > It seems to me like generics are extremely important for
| "library code", and not super important for "application
| code"
|
| I find that it really depends a lot on the language you're
| working in, and how well it does generics.
|
| In Java, I don't use generics much beyond collections,
| streams, stuff like that. Whenever I try, I tend to trip over
| its relatively limited implementation of the concept.
|
| In a language like F#, on the other hand, generics are the
| cornerstone of my business domain modeling. They provide a
| way to map everything out in a way that is much more concise,
| readable, type-safe, and maintainable than I find to be
| possible in many other languages.
|
| I have yet to kick higher-kinded polymorphism's tires in a
| good context, but I can see where a good implementation of it
| would move things even further in that direction.
|
| _(edit: Disclaimer: This isn 't meant to be a statement on
| Go or the advisability of this proposal. Go isn't really
| meant for the kinds of applications where I've seen real
| benefit from generics.)_
| brundolf wrote:
| Whether you find yourself using them and whether they're
| actually necessary are two different things :)
|
| I've gotten use out of generics in "application code", but
| I've also been bewildered by overly-complex generics-
| within-generics-within-generics written by other people in
| application code. It's hard to be conclusive, but I
| wouldn't be surprised if they've done more harm than good
| across application contexts.
| mumblemumble wrote:
| > overly-complex generics-within-generics-within-generics
|
| To me, that's a shining example of the problems I've run
| myself into when trying to squeeze much power out of
| Java-style generics. I never seem to encounter similar
| problems in F#. Scala, it depends on how successful I am
| at not losing a boot in the mud.
|
| Generic programming was born in a language whose other
| pioneering features were algebraic data types and an HM
| type system. I've never really seen a first-rate example
| of one that didn't come paired with at least passable
| examples of the others.
| coddle-hark wrote:
| It's a real pain in the ass not having generics any time
| you're working with algorithms and data structures. Linked
| lists? Graphs? Trees? Go is generally quite nice to work with
| but it implementing these basic structures again and again
| with different underlying data types makes me feel like I'm
| writing Java. Which is ironic because, you know, Java has
| generics.
| brundolf wrote:
| I think the idea is that these fundamentals could/should be
| supplied by the standard library
|
| Ironically, despite all their differences, Rust actually
| has a similar situation: it's really hard to write the
| fundamental data-structures in Rust, so they've put a focus
| on having really good standard-library implementations and
| people are generally content using those (in Rust's case
| it's because the borrow-checker makes pointer twiddling
| hard, but the outcome is similar)
| coddle-hark wrote:
| They kind of did this with maps and slices except that
| they baked them right into the language instead of the
| standard library. Like, map is a keyword. The standard
| library doesn't have many data structures at all because,
| well, without generics they're not very useful. There's a
| few things like a linked list and a thread-safe map that
| accept interface{} types but then you're basically
| throwing the type system out the window.
| random5634 wrote:
| I wish they just did a bunch more data structures in the
| language and called it good.
| wtetzner wrote:
| OCaml has generics (and global inference!) and compiles very
| fast.
| jcelerier wrote:
| every language designer who does not understand c++ is doomed to
| reinvent it
|
| I mean, seriously, func F[T any](p T) { ... }
| pkaye wrote:
| What is wrong with that syntax?
| jcelerier wrote:
| ? nothing is wrong, it's just a different way to spell
| template<typename T> void F(T p) { ... }
| moocowtruck wrote:
| can i say T : SomeConstraint ?
| jcelerier wrote:
| in C++ ? sure template<SomeConstraint
| T> void F(T p) { ... }
|
| or just void F(SomeConstraint auto p) {
| ... }
|
| like this for instance: https://gcc.godbolt.org/z/hPM38T
| esarbe wrote:
| No, not acually.
|
| Generics in Go are vastly different than templates in C++.
| They might be used for similar things, but whereas Go's
| generics actually build up on Go's structural typing,
| templates are ... something completely different again.
|
| I mean; C++ templates are Turing complete. They are in the
| same ballpark as Scala's type machinery. And I say that
| with adoration.
| jcelerier wrote:
| > I mean; C++ templates are Turing complete.
|
| that's not a very high milestone to achieve. Even java
| generics are turing complete
| (https://arxiv.org/abs/1605.05274)
| esarbe wrote:
| Holy crap, indeed!
|
| The older I get, the more icky I find subtyping. It just
| makes things messy...
| tomlu wrote:
| This proposal is fundamentally different from C++ templates as
| they exist today. You don't just chuck a type in there and let
| the compiler have a go at it, SFINAE style.
| anderspitman wrote:
| I've been heavy into Go the past year. I love the simple
| interfaces they've built over some rather complicated things
| (concurrency, cross-compilation, networking, etc), which really
| do tend to just work.
|
| I fear that Go will eventually turn into something where we look
| back and realize we've lost something important by gaining a lot
| of less importants. The impulse to _change things_ is just too
| strong these days. C89 has done just fine unchanged for 30 years.
|
| All I want is a C+=1 I can rely on for the next 30 years.
| dangoor wrote:
| > The impulse to change things is just too strong these days.
|
| FWIW, I don't think this impulse is there with the Go team. Go
| progresses quite slowly, from what I've seen as a user over the
| past 18 months. Generics have been in discussion for _a long_
| time with multiple implementations and no real rush to "just
| ship it".
| anderspitman wrote:
| I was referring to software culture in general, not Go
| specifically. Sorry I wasn't super clear.
| nine_k wrote:
| With all the CVEs we see every month due to what can be only
| called design flaws in C, I have a hard time saying that C did
| _fine_ for last 30 years.
|
| For C+=1, I'd look at Zig; it unfortunately lacks the excellent
| corporate support that Go enjoys.
|
| To me, Go looks much like early Java, only with a much better
| concurrency and saner "OOP". If anything, generics made Java
| better in many ways, without sacrificing performance or
| usability. It took 7 years for Java; it's going to take closer
| to 10 years for Go, bur better late than never.
| bcrosby95 wrote:
| I think they're talking about the simplicity of C.
|
| But yeah, I like to think of Go as a Java for the new
| millennium.
|
| We're a Java shop and lots of people hate some of the newer
| changes to the language. And how OOP focused it is. I think
| Go would be a better fit because it seems to match the
| philosophy of our team more. But don't really think it's
| worth the switch for us.
| anderspitman wrote:
| I've used Java and Go. I find Go a far superior experience.
| Part of that is the standard library which seems to strike
| a perfect balance providing what you need but not too much.
|
| I also think a lot of it has to do with the _culture_ of
| the languages. Kotlin is a pretty nice language, but using
| it for Android still makes me want to hit my computer with
| a hammer because the over-abstraction of the Java ecosystem
| is maddening.
| AlphaSite wrote:
| I was under the impression modern Java was becoming more
| functional than more OO (or at least de-emphasising
| inheritance).
| qsort wrote:
| That's definitely the path they're headed towards.
| @FunctionalInterface from Java 8 and type-inference from
| Java 11 and instanceof destructuring from Java 14 are the
| big changes. "Proper" modern Java is functional, at least
| at the surface level.
|
| The big issue is that the ecosystem is kind of stuck;
| it's freaking 2021 and we're still targeting Java 8. Also
| the type system could be better, null references are
| everywhere even though there is Optional<T> (I wish it
| had optional strict null checks a la Typescript, but I'm
| not sure how realistic that is), and the fact that you
| can't have List<int> because generics won't work on
| primitives is just boneheaded.
|
| But yeah, the language isn't the "Dog extends Animal"
| bullshit they teach you in school, at least not anymore.
| bmurphy1976 wrote:
| It doesn't matter. Eventually you will need to use some
| library that's from the old days. You can't escape the
| abstraction. Not everything has been or can be updated to
| modern standards so just like C++ eventually every bit of
| the language ecosystem will come back to haunt you.
|
| Go doesn't suffer from this yet because it's too new and
| the philosophy is different which keeps some of it at
| bay, but it's only a matter of time. Entropy always wins.
| anderspitman wrote:
| > With all the CVEs we see every month due to what can be
| only called design flaws in C, I have a hard time saying that
| C did fine for last 30 years.
|
| We can argue endlessly about which metrics of success are
| most important.
|
| Zig looks cool. I've seen it mentioned a few times over the
| years. Looks like manual memory management is the default,
| yeah? That's important IMO if you're really trying to replace
| C. Rust is great but I just can't iterate fast enough (yet).
| Does Zig offer an optional GC?
|
| EDIT: Also I never said Go is a good C replacement. But I'm
| finding it useful for some of those tasks and suspect it will
| be less useful for them down the road.
| eplanit wrote:
| I wonder how long it will take for Go to become like Java by
| adding baggage like this.
|
| As soon as you see Enterprise Go, it's over.
| oefrha wrote:
| Related earlier discussions:
|
| - https://news.ycombinator.com/item?id=20576845 (2019 draft)
|
| - https://news.ycombinator.com/item?id=20541079 (also 2019 draft)
|
| - https://news.ycombinator.com/item?id=23543131 (2020 draft, i.e.
| the base version of the current draft)
| karmakaze wrote:
| Also the video[0] linked in the post seems to be ~Dec 2020
| which is new to me.
|
| [0] https://www.youtube.com/watch?v=TborQFPY2IM
___________________________________________________________________
(page generated 2021-01-12 23:01 UTC)