[HN Gopher] The Next Step for Generics
       ___________________________________________________________________
        
       The Next Step for Generics
        
       Author : ainar-g
       Score  : 289 points
       Date   : 2020-06-16 19:34 UTC (3 hours ago)
        
 (HTM) web link (blog.golang.org)
 (TXT) w3m dump (blog.golang.org)
        
       | andy_ppp wrote:
       | I wish they would just add macros instead and then you can
       | effectively code generate at compile time any specific generic-
       | ness that is needed.
        
         | zenhack wrote:
         | Macros are not a substitute for parametric polymorphism
         | ("generics"). With lisp-style macros you could easily implement
         | something like C++ templates, but that's different in at least
         | one critical way: with C++ templates, the templates are
         | expanded _before_ typechecking, whereas with proper generics,
         | typechecking happens while the type parameters are still
         | opaque. The former has several disadvantages:
         | 
         | - It gets you really garbage error messages, because the type
         | errors are usually about something in the body of the template,
         | wherever it tries to do something that the type you substituted
         | doesn't support, rather at the offending call site.
         | 
         | - It hurts compile times (above and beyond proper generics),
         | since you need to type check the generic function at every call
         | site, rather than just once.
         | 
         | - It makes it easy to break interfaces, because exactly what is
         | required of a type parameter isn't written down anywhere --
         | it's just whatever the body tries to do with it.
         | 
         | (Though it is also true that generics are certainly not a full
         | substitute for macros. I would welcome some mechanism for doing
         | codegen that didn't complicate the build system and was a bit
         | lighter weight than what we have now).
        
         | mhh__ wrote:
         | A proper macro system is arguably much harder to get right than
         | generics.
         | 
         | Unless you mean preprocessor macros in which case I'd rather
         | cut myself.
        
         | moreaccountspls wrote:
         | You can use `go generate` with the AST package and get the same
         | thing.
        
       | peter_l_downs wrote:
       | Looks like a big step in the right direction. The biggest pain is
       | that methods cannot contain additional types, which prevents the
       | definition of generic containers with a map method like
       | func (c Container(T)) Map(transform(type V)(T) V) Container(V)
       | 
       | if you want to see what `Result(T)` and `Option(T)` look like
       | under this proposal, check out a very stupid implementation here
       | https://go2goplay.golang.org/p/dYth-AQ0Fru It's definitely more
       | annoying.
       | 
       | But, as per
       | https://go.googlesource.com/proposal/+/refs/heads/master/des...
       | it looks like maybe they will allow additional type parameters in
       | methods, which would be REALLY nice.
        
       | binwiederhier wrote:
       | I am glad contracts are being dropped. It was very confusing
       | indeed. Thanks for listening to the community.
        
       | cies wrote:
       | I cant help feeling it is a missed opportunity to add generics to
       | Go this late. A mistake that is copied from earlier languages
       | (C++, Java), a mistake similar to other mistakes Go chose not to
       | solve at it's inception, like: having implicit nulls (C, C++,
       | Java, JS, C#), lack of proper sum types (C, C++, Java, JS) and
       | only one blessed concurrency model (JS).
       | 
       | While I think I get the reasons for these decision in theory,
       | make a simple-to-fully-understand language that compiles
       | blazingly fast, I still feel it's a pity (most) these issues
       | where not addressed.
        
         | simias wrote:
         | I feel the same way, but then again Rust (among others) exists
         | so it's not like those of us who dislike this approach are
         | "stuck" with Go. I think it's actually nice to have the choice,
         | reading the comment in this thread it's pretty clear that there
         | are people who don't feel like we do.
         | 
         | Go clearly values "simple and practical" over "elegant". It
         | seems to be quite successful at that.
        
           | totalperspectiv wrote:
           | I agree with the spirit of what you are saying, but I'd nit
           | pick and say that lack of generics and presence of implicit
           | null types make Go not simple and not practical over other
           | options in the same space.
        
         | nine_k wrote:
         | If you copy most of your design from Pascal / Modula 2 / Oberon
         | as a safe bet to use a time-proven approach, this is only
         | natural. If you don't want to use a time-proven approach, you
         | need to design your own, and it's a _massively_ more complex
         | and fraught enterprise than adding GC and channels (which are
         | both old, time-proven designs, too, just from elsewhere).
         | 
         | You could say that one could maybe copy the time-proven OCaml.
         | But OCaml doesn't have a proven concurrency story, unlike
         | Oberon and Modula-2 (yes, it had working coroutines back in
         | 1992).
         | 
         | I _also_ wish all these design decisions would not have been
         | made in a new language, like they haven 't been made in Rust.
         | Unfortunately, the constraints under which creators of Go
         | operated likely did not allow for such a luxury. As a result,
         | Go is a language with first-class concurrency which one can get
         | a grip of in a weekend, quick to market, and fast to compile.
         | Most "better" languages, like Rust, OCaml, or Haskell, don't
         | have most of these qualities. Go just fills a different
         | segment, and there's a lot of demand in that segment.
        
           | ernst_klim wrote:
           | >As a result, Go is a language with first-class concurrency
           | which one can get a grip of in a weekend
           | 
           | Which is a mess. Sending mutable objects over channels is
           | anything but "proven concurrency story".
           | 
           | Both Rust and Haskell (and OCaml, if we talk about
           | concurrency and not parallelism) have way better concurrency
           | story than Go. I don't care how fast one could start to write
           | concurrent and parallel code if this code is error prone.
           | 
           | The only difference between Rust/Haskell and Go is that the
           | former force you to learn how to write the correct code,
           | while the latter hides the rocks under the water, letting you
           | hit them in production.
        
           | cies wrote:
           | > first-class concurrency
           | 
           | That's an overstatement.
           | 
           | Also implicit nulls are not beneficent to anyone. And sum
           | types could have made results (error/success) so much nicer.
           | I see no reason to go with nulls at Go's inception, hence I
           | call it a mistake.
        
         | chubot wrote:
         | The flip side was on the top of HN yesterday:
         | 
         |  _Generics and Compile-Time in Rust_
         | 
         | https://news.ycombinator.com/item?id=23534974
         | 
         | It's easy for spectators / bystanders to call something a
         | mistake because you don't understand the tradeoffs. Try
         | designing and implementing a language and you'll see the
         | tradeoffs more clearly.
        
         | ChrisMarshallNY wrote:
         | Swift had generics in place from Day One. It also hides the
         | generics when they don't need to be shown (like many collection
         | types).
         | 
         | I think that most of the standard language features are based
         | on generics, but you would never know it, as a user of those
         | features.
        
           | apetrovic wrote:
           | To be fair, Swift have a bit strange generics implementation
           | that forces programmer to jumps through hoops to achieve
           | something quite common in similar programming languages. The
           | whole standard library is peppered with AnyThis and AnyThat,
           | the language doesn't have generators (yield) and I'm not sure
           | they're possible with the current generics design and
           | programmers needs to learn what is type erasure just because
           | the core team decided that returning generic interface from a
           | function is something nobody will want.
           | 
           | I like Swift a lot, for many reasons, but generics design
           | isn't one of these reasons.
        
             | ChrisMarshallNY wrote:
             | Fair 'nuff. I was always a fan of the way C++ did it, but
             | it was a great deal more technical.
        
         | apta wrote:
         | > having implicit nulls (C, C++, Java, JS, C#),
         | 
         | > lack of proper sum types (C, C++, Java, JS)
         | 
         | Incidentally, Java and C# have addressed (or are in the process
         | of addressing) both issues. Both languages/platforms are
         | superior to golang in almost every conceivable way.
        
           | karmakaze wrote:
           | I'm not aware of any current Java project on Sum types. There
           | is multi-catch for exceptions but no immediate plans I know
           | to use elsewhere.
        
             | apta wrote:
             | A form of pattern matching and switch expression has
             | already made it to the language as of JDK 14. Those are
             | paving the way for full blown pattern matching and sealed
             | types:
             | 
             | https://cr.openjdk.java.net/~briangoetz/amber/datum.html
             | 
             | https://openjdk.java.net/jeps/360
        
             | richardwhiuk wrote:
             | Java aren't fixing nulls, IMHO. They are making it worse.
        
               | apta wrote:
               | How are they making it worse?
               | 
               | In any case, there already exist solutions in place:
               | 
               | https://checkerframework.org/manual/#example-use
               | 
               | https://github.com/uber/NullAway
        
           | [deleted]
        
           | balfirevic wrote:
           | Sum types are not planned for C# 9, are they?
        
           | spion wrote:
           | Except for green threads.
        
           | matwood wrote:
           | I've used a lot of Java and C#, and a decent amount of Go.
           | I'm not sure I would call Go inferior. The design goals were
           | different. I'm also not a language wonk, so maybe that's why
           | I enjoy the relative simplicity of Go. The developer loop of
           | code, run, code is very fast, and the standard library is
           | good out the box. I just want to write code and get stuff
           | done. To that end, Go is another capable, workman type
           | language (like Java or C#).
        
         | jasonhansel wrote:
         | Go really should have learned 2 lessons from Java 5:
         | 
         | 1. People will eventually want generics 2. Retrofitting
         | generics onto an existing language is hard and leads to unusual
         | problems
         | 
         | (edit: I'm glad Go is doing this, but...Java learned this in
         | 2004.)
        
           | mikelward wrote:
           | If this follows the monomorphic approach described in
           | Featherweight Go [1][2], they will at least avoid problems
           | caused by type erasure and avoid too much runtime overhead.
           | 
           | 1. https://arxiv.org/abs/2005.11710 2.
           | https://news.ycombinator.com/item?id=23368453
        
             | jasonhansel wrote:
             | But then you have compile time overhead (an issue Rust and
             | C++ have faced). One of Go's design goals was to have very
             | fast compile times, which might be in doubt if they take
             | the monomorphization approach.
        
               | [deleted]
        
           | Ericson2314 wrote:
           | Go is just Java repeated as farce. The histories are almost
           | identical with ~10 years lag.
           | 
           | We all called this when Go was created, too.
        
           | gowld wrote:
           | Are you saying Go should have not launched in 2009, but shout
           | have waited 10 years until generics were ready?
        
             | jeremyjh wrote:
             | If they made it a priority they could have shipped in 2010
             | with generics. There is no new art in this design.
        
           | kjksf wrote:
           | There's is a design document for Go generics.
           | 
           | If you see "unusual problems" with the design, then tell us
           | what they are.
           | 
           | Otherwise it's just shallow pattern matching "Java added
           | generics late, they had problems, Go added generics late
           | therefore they'll have problems too".
           | 
           | Counterexample: C# added generics late and it's perfectly
           | fine design.
           | 
           | The reason Go team is not rushing to implement generics is
           | precisely so that the design makes sense, in the context of
           | Go.
           | 
           | Over the years Ian Taylor wrote several designs for generics,
           | all of which were found to not be good enough.
           | 
           | They are doing it the right way: not shipping until they have
           | a good design and they didn't have good design when Go
           | launched.
        
       | [deleted]
        
       | nemothekid wrote:
       | I've been writing a lot of Rust lately after writing Go for 7
       | years now (unrelated, but that is incredibly odd for me to write
       | out. It still feels like that funny new language that came out of
       | Google). I've always said that generics is overhyped (in Go), but
       | I find that I write a lot of generic stuff in Rust that is
       | somewhat surprising. For example we have a JSON API that returns
       | data in the format '{"ok": bool, "data":...}'. In Go I might do
       | something with json.RawMessage and pay for decoding twice, or
       | annotate every struct with Ok/Data. But in Rust I can just have a
       | type ApiResponse<T>. Then I can have functions that operate just
       | on ApiResponse, or ApiResponse<T=Users>. This problem is solvable
       | in Go, but in a different way and with less guarantees. However
       | that power comes at a mental cost, that creeps in C++ as well. I
       | spent more time playing type system golf trying to come up with
       | the optimal type for whatever usecase. In Go I might just "do the
       | work", but in Rust I've turned 5 minute functions into 30 minute
       | api design thought exercises. The jury is out if thats is a good
       | thing or a bad thing.
       | 
       | That said, the only feature I'd steal from Rust is sum types and
       | getting rid of null. `nil` interfaces are the only language
       | feature to actually cost me money and Option<T> is just better in
       | every aspect, and Result<T> is much better than err == nil. I'd
       | be perfectly happy with compiler blessed Option and Result types
       | even if the larger language didn't support generics.
        
         | dom96 wrote:
         | I like to think that how much time a language makes you spend
         | designing the API is a spectrum, and Rust in particular is very
         | much towards the extreme. There are a lot of languages out
         | there with generics that do not place the cognitive load that
         | Rust's borrow checker puts on you. I suggest you give those
         | languages a try if you feel that 30 minutes is too long.
         | 
         | I must say I am biased, since I develop a language that I
         | believe fits the sweet spot here. I would encourage you to give
         | it a try: https://nim-lang.org
        
           | munificent wrote:
           | Instead of a spectrum, I think of it more qualitatively. Rust
           | says that lifetime is a fundamental, necessary part of an
           | API's design. To call an API, you have to think about the
           | ownership of what you send to it and what you receive.
           | 
           | C also makes you think about ownership in your API design,
           | it's just the language doesn't give you any real tools to
           | express or enforce it. C++ gives you a bunch of tools and
           | options, but you have to hope that you and the API you want
           | to use agreed on which subset of the tools to use.
           | 
           | Garbage collected languages specifically take memory
           | management _out_ of the API design by declaring that the
           | runtime will take care of it for everyone.
           | 
           | If you _want_ lifetime to be something an API can control,
           | then I think Rust 's approach makes sense even though it
           | obviously adds complexity. If you don't, then, yes, removing
           | it from the equation definitely lowers the API design burden.
           | 
           | In many ways its analogous to having language support for
           | strings. In C/C++, you gotta hope that the library you're
           | using has the same approach to strings that you want
           | (std::string? char*, wchar_t?, something else?). In newer
           | languages, it's just a given. (For better or worse: because
           | then you end up stuck with UTF-16 in some languages.)
        
           | kenhwang wrote:
           | I've always said if you can write Ruby/Scala, you can
           | probably write simple Rust with very similar levels of
           | productivity after you get past the initial learning curve.
           | But apparently there's a sizable population that thinks
           | Ruby/Scala is hard/confusing/sizable cognitive load too.
        
           | pasabagi wrote:
           | I don't consider myself a particularly good programmer, and I
           | don't find the burrow checker is a substantial source of
           | cognitive load.
           | 
           | I think I get way more friction from:
           | 
           | - library authors that go crazy with generics.
           | 
           | - the million ways rust makes performance tradeoffs explicit
           | (even when you're writing part of the program where
           | performance simply doesn't matter).
           | 
           | - the million-and-one things you can do with an option type.
           | 
           | - the arcane macro syntax.
           | 
           | I could probably go on. I've been writing a lot of rust
           | because I wanted a low-ish level language with good C
           | interop, and I didn't want to learn C++, so I wasn't at any
           | point a rust fanboy. I very much appreciate not having to be
           | a genius to avoid segmentation faults, but other than that, I
           | think rust is a somewhat ugly language - for the simple
           | reason that it has a lot of features, some of which partially
           | overlap, and most of which are vaguely inconsistent.
           | 
           | I'm probably sounding a bit harsher than I mean to -
           | obviously, you can't expect a language like rust to have the
           | simplicity and consistency of something like lua, and by and
           | large, it's better than anything else I write programs in.
           | 
           | Still, the point is, as somebody with a lot of (fair and
           | unfair) complaints, the burrow checker is not one of them. It
           | complains rarely, and when it does you're almost always doing
           | something stupid, unless you're doing something fairly
           | arcane, in which case, you should probably use unsafe.
        
             | bcrosby95 wrote:
             | Then you're a good programmer.
             | 
             | Rust effectively forces you to code like you're writing a
             | multi-threaded app even when you aren't. There's a reason
             | why people suck at writing multi-threaded apps: because
             | it's hard. This is what people are fighting when they fight
             | the borrow checker. And this is why so many people find it
             | frustrating. There's all sorts of designs that flat out
             | don't work or are way more effort than they're worth.
        
         | d0m wrote:
         | From someone who hasn't learned/considered Go for any real
         | project so far, would there be any benefits in going Go instead
         | of Rust? Or learning Go instead of Rust?
         | 
         | The way I look at it, and I could totally be wrong, is if you
         | go for a different language for your back-end than your front-
         | end, it betters pay off big time. I find Go and Typescript to
         | be in a very similar ballpark in term of productivity, but with
         | Typescript having a huge advantage since you can reuse all the
         | learning/tooling from the front-end.
         | 
         | Rust on the other hand really adds something to the table with
         | the memory safety and zero-cost abstraction.
         | 
         | TL&DR: It feels like either you eat the bullet and go Rust for
         | memory safety, or Typescript is a better choice. Of course I'm
         | overly simplifying.
         | 
         | Thoughts?
        
         | vlunkr wrote:
         | Do we have a name yet for the internet phenomenon where every
         | time Go is discussed, Rust must be immediately brought into the
         | conversation, and vice versa?
        
           | atdt wrote:
           | gorrosion!
        
           | jeffrallen wrote:
           | "oxidation"
        
           | bsder wrote:
           | No, but I wish I saw Go vs Clojure, more often.
           | 
           | To me, Go vs JVM ecosystem languages is a much more difficult
           | choice. I would have to actually think a bit.
           | 
           | To me, Rust and Go really don't occupy the same ecosystem
           | niche. I can't think of a project where I would have to
           | choose between them--the choice between them is almost always
           | _dead obvious_ given the project description.
        
             | zucker42 wrote:
             | Clojure and Go seem like two of the least comparable
             | languages to me. They basically have no commonalities.
             | Comparing Java, Kotlin, or Scala to Go seems more valuable
             | to me.
        
           | chooseaname wrote:
           | It is called HN. You can't _ever_ discuss a language without
           | also discussing Rust.
        
           | munificent wrote:
           | It shouldn't be that surprising. Both are "low-level"
           | languages that came onto the scene around the same time, are
           | highly opinionated, but stake out very different points on
           | the design space.
           | 
           | If you start talking about changing the recipe for Coca-Cola,
           | surely Pepsi is going to enter the discussion at some point,
           | right?
        
             | gver10 wrote:
             | This doesn't explain the obsession with Rust.
             | 
             | Fitting analogy: If there's an article about McDonald's,
             | people would always talk about Wendy's instead. No one
             | would mention BurgerKing (C++), Subway (D), KFC, ...
        
               | viraptor wrote:
               | I see zig and d mentioned a lot on the rust/go posts
               | about features and C++ on the posts about performance. It
               | doesn't seem to be go/rust specific.
        
           | asjw wrote:
           | I think the word you're looking for is _marketing_
        
           | gver10 wrote:
           | Not a 100% fit, but we have:
           | https://en.m.wikipedia.org/wiki/Cargo_cult_programming
           | 
           | > a style of computer programming characterized by the
           | _ritual_ inclusion of code or program structures that serve
           | no real purpose.
        
           | MrRadar wrote:
           | They both came on the scene at the same time, aiming for
           | similar (though not exactly the same) markets, with Go aiming
           | for something between a "better C" and "statically-compiled
           | Python" and Rust a straight-up "better/safer C++". There's
           | enough overlap between those two markets that it's natural
           | for people to want to compare the two languages and how
           | they've evolved over time.
        
             | ChrisSD wrote:
             | To be clear Mozilla first mentioned starting sponsorship
             | for something called "Rust" a few months after Go was
             | publicly announced. However that prototype "Rust" was a
             | language almost entirely unlike Rust 1.0 (which didn't
             | appear until 2015, five years after sponsorship was first
             | announced). Go was already years into development before
             | being announced and was used internally before the public
             | 1.0 release.
             | 
             | Today Rust and Go exist in different spaces. Go is a
             | Python/Ruby/Java/etc alternative. Whereas Rust is an
             | alternative to C/C++/etc. Obviously there is overlap
             | between those sets of languages but that doesn't really
             | mean Rust and Go are as directly comparable as HN comments
             | might lead people to believe.
        
               | jfkebwjsbx wrote:
               | +1
               | 
               | I don't understand people trying to make "a language that
               | does it all". There has been a lot of that in Go, in
               | Rust, in JavaScript, in Java, in TypeScript, in C++, in
               | Haskell, in D...
        
         | gowld wrote:
         | Go has had generics for years. But they are in a preprocessor
         | not main compilation.
        
           | jasonhansel wrote:
           | Indeed. If Go had a built-in macro preprocessor, generics
           | would be less necessary, but macro programming has plenty of
           | problems of its own...
        
         | shpongled wrote:
         | Funny, I also have an `ApiResponse<T>` in my backend Rust code
         | that serves exactly the same purpose (same name even) !
        
         | aplummer wrote:
         | I can dramatically shorten development time with generics +
         | codable in Swift using a similar approach - to the same code in
         | objective C it's like night and day
        
         | mov_ wrote:
         | I can relate, maybe this is the paradox of choice?
         | 
         | That said I agree completely, `Option`/`Result` themselves
         | (even if they are just intrinsic language elements, not user-
         | implementable types) are valuable.
        
         | vlads_ wrote:
         | Fun fact: because json is a reflective package in Go, solving
         | your problem is quite trivial:
         | https://play.golang.org/p/-TZ5b9Su1or .
         | 
         | As for programming with types, that's partly what Go was trying
         | to avoid: https://news.ycombinator.com/item?id=6821389 . And I
         | agree with Pike on this one. The nice thing about Go for me is
         | that I'm just writing code. Not defining a type hierarchy, not
         | rewriting some things to get them just right, not defining
         | getters, setters, move and copy constructors for every type :).
         | Just telling the computer what to do and in what order. When
         | I'm writing a library I'm defining an API, but that's about it;
         | and you can usually steal whatever the standard library's
         | patterns are there.
         | 
         | I disagree about nil as well; I think Go's zero value approach
         | is useful, and basically impossible without nil (it wasn't
         | necessary in my code, but I may want to instantiate an
         | ApiResponse object without a data structure ready to be passed
         | in).
         | 
         | A little bit of a rambly response from me, but, all in all, I
         | think I'll be one of the stubborn ones which refuses to use
         | generics in his code for a long time.
        
         | nurettin wrote:
         | Substituting null with optional helps with getting a stack
         | trace of what's going on. However, it doesn't get rid of
         | programming errors.
         | 
         | What does get rid of errors is IDE support for nullable types
         | which warns the programmer that they have accessed a property
         | without checking for nulls. Take kotlin and Intellij/IDEA for
         | instance.
        
         | nine_k wrote:
         | The cost you pay for appeasing the type system is the cost
         | you're not paying writing more tests around corner cases ("this
         | function never returns null"), debugging segfaults, null
         | pointer exceptions, "attribute not found" errors, memory leaks
         | (lifetimes help here), etc.
         | 
         | The type system may not always help you get your code out the
         | door faster, but when you finally ship it, you have much easier
         | time running it.
        
           | atdt wrote:
           | [[citation needed]]. "Everyone knows" that type systems
           | reduce bugs, but actual evidence for that is pretty weak.
           | 
           | https://danluu.com/empirical-pl/
        
             | sethammons wrote:
             | That study calls out significant issues. I can go from
             | personal experience. Porting python to Go. In python, we
             | had to have tests dedicated to ensuring the right types
             | were handled the right way. Is it a string or an array
             | passed in? Welp, need a new test to cover that. Whole
             | classes of tests go away with more advanced type systems.
             | Those tests were needed to prevent those classes of bugs.
             | So better type system == removing of whole class of bugs.
        
               | sixstringtheory wrote:
               | Came here to say I've had this exact same debate. We were
               | writing long Python test suites to check types. I
               | constantly (and playfully, I don't care to die on this
               | hill) pointed out this is fully automatable with a static
               | typing system. I remember many times being told to just
               | read thousands of lines of uncommented tests in order to
               | understand some thing I was trying to debug. (Don't get
               | me started on Python stack traces!)
        
               | pfdietz wrote:
               | But, do you really save tests? You have to test your code
               | anyway. Does a type system buy you anything more in code
               | that's going to be heavily tested for other reasons?
        
           | Thaxll wrote:
           | That's up to debate, you have to prove that in x.y.z language
           | the time spend for maintenance would have been avoided if
           | x.y.z had that feature from the start. Especially language
           | like Rust that have a big learning curve / blockers, is it
           | justified on the end? I'm not sure.
           | 
           | In Go I never felt that omg it misses some big features we're
           | screwed, we're going to pay the cost of running that in Prod.
           | All the services I worked on while not being perfect ran like
           | a clock without issues on the long term, and it has to do
           | with Go "simplicity", eventhough you never wrote the code you
           | can actually jump into any code base without issues, there is
           | no weird abstraction layer / complexity that takes you day to
           | understand.
           | 
           | For me the fact that Go is a "simple" language is very
           | powerful because on the long run it's much easier to
           | maintain.
        
             | throwaway894345 wrote:
             | Moreover, not all domains require you to catch every error.
             | In many applications, it's _just fine_ to ship a few bugs
             | and it 's much better to ship a few bugs here and there
             | rather than slow down dramatically to appease a pedantic
             | type checker. This is especially true when bugs can be
             | found and fixed in a matter of minutes or hours and when
             | the bugs are superficial. And I also contend that the more
             | pedantic the type checker, the more likely that the
             | additional bugs that it finds are of diminishing importance
             | --they are less and less likely to be a downtime issue,
             | they are increasingly likely to be in very rarely hit
             | paths.
             | 
             | I like Rust a lot and I hope to get to use it more, but
             | "added type safety at the expense of iteration velocity" is
             | not a good tradeoff for my domain and I suspect many others
             | (although its iteration velocity is improving
             | monotonically!).
        
               | alexbanks wrote:
               | I feel like Rust and Go are fundamentally different use
               | cases and I wish people would stop the language debate.
               | 
               | Rust: For code that isn't meant to be iterated upon, that
               | must be safe/correct while also being performant.
               | 
               | Go: For code that is meant to be iterated upon
               | frequently, that must be performant and easy to maintain.
               | 
               | I would like my tools to be written in Rust, and I would
               | like to interface with them in Go.
        
               | ameixaseca wrote:
               | Which code is not meant to be correct?
        
               | alexbanks wrote:
               | This is a silly strawman. All code is meant to be
               | correct, but often times close enough to correct is good
               | enough (as is literally the whole point of my post). Most
               | businesses operate on code that is close enough to
               | correct for their individual risk tolerance, and
               | increasing that correctness would reduce speed/throughput
               | and increase cost.
        
               | zozbot234 wrote:
               | What's wrong with iterating on Rust? Comprehensive type
               | checking makes refactorings a lot easier than something
               | like Go. And you can iterate starting from a simple,
               | working solution that relies on marginally more expensive
               | features, such as .clone(), Rc<...>, Cell<...> or
               | RefCell<...>, Any for dynamic data etc. etc.
        
               | Ericson2314 wrote:
               | I think you are missing the point of type systems. Our
               | current economy doesn't value correctness at all. Code is
               | rushed out, and usually live longer than it was designed
               | too. This mimicks the the largest infrastructure debt of
               | America (and to a lesser extent, the western world as a
               | whole).
               | 
               | But the same shoddy system is also constantly changing
               | the requirements, precisely because nothing is built for
               | posterity and there's tons of make-work churn. Good type
               | systems make code far easier to refactor, and refactoring
               | (as opposed to slapped-on new code) is the only way to
               | handle shifting requirements without being crushed under
               | ones own weight.
        
               | alexbanks wrote:
               | I'm not missing anything. I'm well aware of the value of
               | type systems, as I am our current cultural decision to
               | value speed over correctness.
               | 
               | I'm not confident that average level developers are able
               | to ship features in Rust as quickly as they would in Go,
               | which is the root of my point. Those building tools,
               | which generally do not have the same success metrics with
               | respect to speed and correctness, should use the language
               | with a focus on safety and correctness. Those whose
               | positions do not lend themselves to safety and
               | correctness should not use that tool.
        
               | zozbot234 wrote:
               | > I'm not confident that average level developers are
               | able to ship features in Rust as quickly as they would in
               | Go
               | 
               | Even if this was a genuine concern, they should still use
               | Rust. Because it will be far easier to refactor the code
               | that they ship into something that _is_ safe and correct,
               | compared to rewriting a Go codebase.
        
               | alexbanks wrote:
               | We can agree to disagree. The whole point of my post is
               | that I do not agree with your statement.
        
               | kenhwang wrote:
               | Strangely, I found Go to be one of the least productive
               | languages I've ever used, 2nd only to Java. It just makes
               | the programmer do so much of the work for common tasks,
               | and it's incredibly repetitive and while it generally
               | generates a fast program, the quality/fault rate isn't
               | any better than any other iterative language.
        
               | sethammons wrote:
               | curious what other languages and domains you are working
               | in. In my experience, Go has been a boon to productivity.
               | I do networked services mostly. Projects that are
               | maintained for years with lots of tweaks. Rewrites,
               | greenfield. Lots of focus on operability,
               | maintainability, and high availability. My work started
               | in php, became more serious under perl (anyevent), and
               | python (twisted), and toss in a smattering of other
               | things here and there (some C, C++, ruby, javascript,
               | lua). The main work horses were perl and python though.
               | I've been using Go now since 1.2 (around 2013/2014).
               | Every time I go back to these other languages in the same
               | domain it is like taking a giant leap backwards.
        
               | zozbot234 wrote:
               | Go is most often preferable to any of the languages you
               | mentioned, no doubt about it. It's just that Rust is
               | generally held to an even higher standard.
        
               | Ericson2314 wrote:
               | So you've only used untyped languages, C++? No wonder you
               | think Go is productive...
               | 
               | Not that Java is even good, but that beats go. The
               | history of Go is the history of Java, repeated as farce.
               | It's even some of the same academic lineage come to bail
               | things out with the type system for generics!
        
               | nine_k wrote:
               | (1) This is why you can even ship code written in JS or
               | Python. (2) This is why Typescript and mypy exist.
        
             | amelius wrote:
             | Ideally, you'd have a language that allowed you to do both.
             | Generics/type safety when you want them (e.g. for stuff
             | that is hard to maintain), and total freedom otherwise.
        
           | whatshisface wrote:
           | > _The cost you pay for appeasing the type system is the cost
           | you 're not paying writing more tests around corner cases_
           | 
           | I have spent a lot of time in Rust getting carried away with
           | type system stuff that is not required for the actual program
           | I am writing. I realize this when I go over it a second time
           | and reduce line counts drastically by replacing my gee-whiz
           | generics with primitive types. This is balanced out by an
           | equal number of times where I've reduced line counts
           | drastically by using the advanced type stuff to reduce the
           | repetition that comes along with using primitives to
           | represent a type that has way more structure than the
           | primitive.
        
             | hinkley wrote:
             | A year from now, isn't it the 'reduced line count' that
             | will have value to you, rather than the churn?
        
               | WJW wrote:
               | Sometimes. Often the reduced line counts contain way more
               | complexity per line and understanding it is just as hard.
               | There's something to be said for code churn, as it keeps
               | it fresh in the mind of whomever is on the team right now
               | as opposed to "a guy that left eight years ago wrote
               | this".
        
               | hinkley wrote:
               | As simple as possible, but no simpler.
               | 
               | I sometimes use the analogy of haiku versus short story.
               | 
               | You can pack a lot of meaning into a haiku, but few will
               | be able to unpack it in the manner intended. A short
               | story can get you to the point without filling a whole
               | book to do so. And a lot more people can write a
               | reasonable short story.
        
               | [deleted]
        
             | chewbacha wrote:
             | > I realize this when I go over it a second time and reduce
             | line counts drastically by replacing my gee-whiz generics
             | with primitive types.
             | 
             | I do this too, but I've also come to realize that fresh
             | perspectives and newly learned patterns may be more of the
             | reason than being a little over zealous.
             | 
             | Edit: forgot to mention that this happens to me in most
             | languages, not just rust.
        
         | bradstewart wrote:
         | > I spent more time playing type system golf trying to come up
         | with the optimal type
         | 
         | > The jury is out if thats is a good thing or a bad thing
         | 
         | Well said. I have this same thought with TypeScript on a
         | frequent basis.
        
           | cambalache wrote:
           | I am just learning the ropes of Typescript with React and
           | Christ sometimes it is hard not to think it is a veritable
           | clusterfuck.
        
         | mrmonkeyman wrote:
         | Same here. Go tends to make me "just do it". I personally think
         | it's a big win, because I believe systems need to be composed
         | and the components they are composed of as simple as possible.
         | As a human though I tend to waste time coming up with fancy
         | abstractions. Generics are exactly that. Fancy, nice to have,
         | but I completely fail to see how they are necessary.
        
           | platinumrad wrote:
           | I mostly disagree with this post, but vouched for it because
           | there is absolutely no reason for it to be dead.
        
           | totalperspectiv wrote:
           | Is there an old quote about `as simple as possible but no
           | simpler`? Go rides the simple line really well occasionally,
           | but generics is an area where it falls into the 'simpler'
           | category.
        
       | galkk wrote:
       | Typical to Go verbose style, but even that is better than nothing
       | (example from the doc)                   s := []int{1, 2, 3}
       | floats := slices.Map(s, func(i int) float64 { return float64(i)
       | })                   Type inference for generic function
       | arguments         This is a feature we are not suggesting now,
       | but could consider for later versions of the language.
        
       | ainar-g wrote:
       | Link to the current draft:
       | 
       | https://go.googlesource.com/proposal/+/refs/heads/master/des...
        
         | Someone wrote:
         | _"A type parameter with the comparable constraint accepts as a
         | type argument any comparable type. It permits the use of == and
         | != with values of that type parameter"_
         | 
         | I think that's an unfortunate choice. Quite a few other
         | languages use the term _equatable_ for that, and have
         | _comparable_ for types that have =, <= and similar defined.
         | Using _comparable_ here closes the door for adding that later.
         | 
         | I also find it unfortunate that they chose                 type
         | SignedInteger interface {         type int, int8, int16, int32,
         | int64       }
         | 
         | as that means other libraries cannot extend the set of types
         | satisfying the constraint.
         | 
         | One couldn't, for example, have one's biginteger class reuse a
         | generic _gcd_ or _lcm_ function.
        
           | coder543 wrote:
           | Languages have been known to use "Ordered", "Orderable", or
           | "Ord" for what you're calling "comparable". Rust and Haskell
           | are both languages that fit this criteria.
           | 
           | The design draft refers to "constraints.Ordered", so they're
           | definitely thinking about having both "comparable" and
           | "constraints.Ordered". Although, for consistency with
           | "comparable", I think it should be called
           | "constraints.Orderable".
        
             | Someone wrote:
             | That's true, but both rust (https://doc.rust-
             | lang.org/beta/std/cmp/trait.Eq.html) and Haskell (https://h
             | ackage.haskell.org/package/base-4.12.0.0/docs/Data-...) use
             | _eq_ or similar for what this proposal calls _comparable_ ,
             | as do C# ( _IEquatable_ ), java (through _Object.equals_ ),
             | and Swift ( _Equatable_ )
        
               | coder543 wrote:
               | Sure, but as someone else already pointed out,
               | "comparable" is an established Go term that refers to
               | "==" and "!=" only, and "ordered" refers to the other
               | comparison operators.
               | 
               | My point was that "comparable" is _not_ universally used
               | in place of the "Ordered" term that the Go team is using,
               | as you were seemingly implying. Ordered is a perfectly
               | fine term for it.
               | 
               | You said:
               | 
               | > Using comparable here closes the door for adding that
               | later.
               | 
               | But the door is not closed in any way. It's just called
               | "constraints.Ordered", which is perfectly reasonable.
        
           | typical182 wrote:
           | FWIW, "comparable" and "ordered" are well defined terms in
           | the Go language specification:
           | 
           | https://golang.org/ref/spec#Comparison_operators
        
             | MHordecki wrote:
             | The vocabulary from that language spec is not even followed
             | consistently in the language's own standard library.
             | 
             | The `strings.Compare`[1] function is used to establish
             | ordering, in the spec sense. You'd think they would name it
             | "Order".
             | 
             | Similarly, the popular go-cmp[2] library provides `Equal`
             | for equality, instead of `Compare`.
             | 
             | [1]: https://godoc.org/strings#Compare [2]:
             | https://github.com/google/go-cmp
        
         | saghm wrote:
         | I'm usually a bit of a skeptic when it comes to Go, but this
         | proposal surprised me; for the most part, or seems like this is
         | exactly what I would want if I were a Go programmer. Being able
         | to define both unbounded generic parameters and parameters
         | bounded by interfaces is key, and while from skimming I'm not
         | sure if it allows giving multiple interfaces as a bound, in
         | practice this shouldn't be a huge issue due to how Go's
         | interfaces are implicitly implemented (which would allow you to
         | define a new interface that's the union of all the other
         | interfaces you want to require, and all the types that
         | implement all of them will automatically implement the new
         | one). Interestingly, the proposal also defines what is
         | essentially a union type interface, which I think is something
         | that Go definitely could use. Although there are a couple of
         | minor warts (e.g. not being able to have generic methods) and
         | the syntax is not what I'd personally pick, overall I'm pretty
         | impressed.
        
         | coder543 wrote:
         | This is the new code branch:
         | https://go.googlesource.com/go/+/refs/heads/dev.go2go
         | 
         | I can't find the actual code review for this. It seems to be
         | hidden/private still?
         | 
         | This was the previous code review: https://go-
         | review.googlesource.com/c/go/+/187317
         | 
         | The comment at the end is where I got the link to the new
         | branch, but as an outsider, I don't have any good way to ask
         | where the new code review is, so I'm leaving this comment here
         | in hopes that a googler will see it and point me in the right
         | direction.
         | 
         | Based on a link that's on the new branch's page, it _might_ be
         | CL 771577, but it says I don 't have permission to view it, so
         | I'm not sure.
        
           | ianlancetaylor wrote:
           | There isn't a code review for the changes on the dev.go2go
           | branch (though you could construct one using git diff).
           | 
           | The dev.go2go branch will not be merged into the main Go
           | development tree. The branch exists mainly to support the
           | translation tool, which is for experimenting with. Any work
           | that flows into the main Go development will go through the
           | code review process as usual.
        
       | hashamali wrote:
       | While I've learned to work without generics, this is a welcome
       | proposal. Many cases where we've been forced to use interface{} +
       | cast could be genercized.
        
       | rowanseymour wrote:
       | The only time I get frustrated by the lack of generics in go in
       | trying to get keys from a map... I can't have a single
       | `keys(map[string]T) []string` function.
        
         | vlads_ wrote:
         | Well, you can :) https://play.golang.org/p/sum21uSy1Ae
        
           | grey-area wrote:
           | For certain values of can.
        
             | vlads_ wrote:
             | Never said it was a good idea. It's inefficient, unsafe and
             | hard to read but, as far as I can tell any generic function
             | with concrete return types can be written using reflect
             | (and any generic function with generic return types can be
             | written also, it just requires the caller to manually cast
             | from an interface{} to the correct type).
             | 
             | So, that's an interesting thing.
        
           | srathi wrote:
           | This is an awesome use of the reflect package!
        
       | daenz wrote:
       | In my mind, this generics flip-flop will do no good for the long
       | term survival of Golang. One way to view it is as "listening to
       | the community and changing" but I think a change this big signals
       | a fracture more than an evolution.
       | 
       | Think about how much planning and foresight goes into making a
       | programming language, especially one that comes out of a massive
       | top-tier tech company like Google. They deliberately chose not to
       | include generics and (from what I remember when I wrote Go code)
       | spent time + effort explaining away the need for them. Now the
       | decision is being reversed, and it signals to me that they don't
       | know what they're doing with the language, long term. As a
       | developer, why do I want to hitch my carts to a language whose
       | core visions are very clearly not correct or what people want?
        
         | etaioinshrdlu wrote:
         | Or alternatively, they took forever to do it not because they
         | don't like generics, but because they are super conservative
         | with design and wanted to do it right.
        
           | shanemhansen wrote:
           | Is there anything about this proposal that would have been
           | surprising to someone 5 years ago? Anything to show for
           | waiting most of the decade other than a decade lost?
        
         | hn_check wrote:
         | From very, very early in the project-
         | 
         | "Generics may well be added at some point. We don't feel an
         | urgency for them, although we understand some programmers do."
         | 
         | ...
         | 
         | "Generics are convenient but they come at a cost in complexity
         | in the type system and run-time. We haven't yet found a design
         | that gives value proportionate to the complexity, although we
         | continue to think about it."
         | 
         | ...
         | 
         | "The topic remains open"
         | 
         | They haven't flip-flopped whatsoever. Now that the language has
         | more thoroughly been fleshed out and community has matured, and
         | various proposals have come and gone, the discussion continues.
        
       | pjmlp wrote:
       | Currently I just want some form of genericity, regardless which
       | one, but I fear we will go through yet another cycle of community
       | feedback and by August 2021 still not there.
       | 
       | However not to be just negative, I will certainly take the
       | opportunity to provide my positive feedback as well, so looking
       | forward to play with go2go.
        
         | zenhack wrote:
         | I'm sympathetic to being somewhat impatient; I'd really like to
         | have generics in Go years ago too. But I'm also glad they
         | didn't just plow ahead with the first draft of the proposal
         | that came out; the way contracts worked was horrible, and the
         | slow deliberative process means that we don't actually have to
         | deal with those mistakes. Unfortunately, good design takes
         | time.
        
         | eatonphil wrote:
         | Yeah, glad that work is being done. But surprised to see this
         | status update be that nothing substantial has changed in the
         | last year.
        
           | badrequest wrote:
           | They've reworked the draft, written a generics-capable type
           | checker, a translator tool that lets you use the draft in the
           | current version of Go, and published a generics-capable
           | version of the playground. How is none of that substantial?
        
       | [deleted]
        
       | meddlepal wrote:
       | What a joke. Go devs spent a decade telling people they were
       | waiting for the right design for Generics to emerge. Finally
       | after a decade they deliver to us... Java-lite generics which are
       | going to require the community to do a massive library ecosystem
       | retooling for next five to ten years similar to how Java felt
       | going from pre 1.5 to 1.5 and beyond.
       | 
       | This language is such a frustrating mess at times.
        
         | atombender wrote:
         | I'm not sure what you expected. With the exception of Ian Lance
         | Taylor, the Go team has expressed skepticism (and, in the case
         | of Rob Pike, outright disdain) about generics since day one.
         | 
         | To anyone who has followed the Go development process at all,
         | this proposal will not be a surprise. The previous proposal was
         | also extremely conservative. Personally, I don't see any other
         | way. Going full-blown generics in the style of
         | Rust/Haskell/OCaml, for example, would drastically alter the
         | language.
         | 
         | This is closer in scope to, say, Modula-3.
        
           | meddlepal wrote:
           | I'm more frustrated that it took a decade to get to this
           | point. Not that it is not more powerful.
        
       | conroy wrote:
       | Here's a four-line replacement for the various `sql.Null*` types
       | in the standard library.                   type Null(type T)
       | struct {             Val   T             Valid bool // Valid is
       | true if Val is not NULL         }
       | 
       | Here's what it looks like in practice:
       | https://go2goplay.golang.org/p/Qj8MqYWWAc3
        
         | sethammons wrote:
         | neat example, thanks. It gives you a generic Null type that has
         | a bool is-valid field. Very similar to the sql.Null{String,Int}
         | types, but you don't have to declare each one. Kinda the point
         | of generics :)
        
       | atombender wrote:
       | Unlike other commenters, I don't think it's too late to add
       | generics to Go. I'm looking forward to this.
       | 
       | My only negative reaction is that throughout these proposals,
       | I've not been a fan of the additional parentheses needed for this
       | syntax. I can think of several syntaxes that would be more
       | readable, potentially at the expense of verbosity.
        
       | cybervasi wrote:
       | Ada language had generics since the first 1983 standard. When C++
       | came about it introduced OOP features that Ada lacked and didn't
       | get for another 12 years when 1995 standard was introduced. C++
       | users never missed generics for many years and the language
       | became ubiquitous while Ada remained marginalized. What's
       | interesting is that all strongly typed languages have now jumped
       | on the generics bandwagon, which to me shows that being ahead of
       | its time doesn't pay off.
        
         | erik_seaberg wrote:
         | The first edition of Stroustrup is the only version without
         | templates (and exceptions). I remember awful macro workarounds
         | for C++ compilers with broken template support, so I think
         | everyone knew that some kind of generics were obviously needed.
         | Ada got this and a few other things right, but it was painfully
         | verbose and the first compilers were so expensive that few
         | seriously evaluated it.
        
       | hitekker wrote:
       | Rob Pike is not mentioned in the blogpost or in the sources the
       | authors acknowledge as contributors to this project.
       | 
       | What is he up to nowadays?
        
         | ainar-g wrote:
         | He still commits code and responds to Github issues
         | occasionally, and I think it's safe to assume that he's still
         | participating in the internal discussions about the design. But
         | most decisions about language changes these days are made by
         | Russ Cox, Ian Lance Taylor, and Robert Griesemer. At least,
         | that's the impression I'm getting.
        
           | hitekker wrote:
           | That jives with this article I found:
           | 
           | https://evrone.com/rob-pike-interview
           | 
           | > Rob: Although it's far from certain, after over a decade of
           | work it looks like a design for parametric polymorphism, what
           | is colloquially but misleadingly called generics, is coming
           | in the next year or two. It was a very hard problem to find a
           | design that works within the existing language and feels as
           | if it belongs, but Ian Taylor invested a phenomenal amount of
           | energy into the problem and it looks like the answer is now
           | in reach.
           | 
           | Sounds like he took a welcomed step back. Hopefully the new
           | leaders can deliver.
        
             | grey-area wrote:
             | Ian Lance Taylor has been involved in the language for a
             | long time, since 2008. Here he is talking about the built-
             | in function append and why it shouldn't really exist in
             | 2017
             | 
             | https://www.airs.com/blog/archives/559
        
         | bjz_ wrote:
         | I think he was the one to ask Phil Wadler to help out on the
         | Featherweight Go formalization work.
        
       | renewiltord wrote:
       | Prefer no generics in Go. Like Go simplicity. Don't want all
       | languages to merge into single grey good form. Want strong trade-
       | offs made.
        
         | shpongled wrote:
         | Why use many words when few do?
        
       | djhworld wrote:
       | Neat, thrown together Set implementation
       | https://go2goplay.golang.org/p/FffAhV8aLyN
        
         | coder543 wrote:
         | Might as well use the zero-size struct{} instead of bool, given
         | that you're not currently using the bool values for anything:
         | https://go2goplay.golang.org/p/9iegVQ2VQCr
         | 
         | Alternatively, use the bool values and rely on the zero value
         | for missing elements:
         | https://go2goplay.golang.org/p/E7yHQqAPseG
        
           | bradfitz wrote:
           | And variadic args to NewSet looks a bit nicer yet at the call
           | site: https://go2goplay.golang.org/p/fTp3W9IwuVP
        
       | rb808 wrote:
       | I like generics for collections but that is about it. I've seen
       | "gifted" programmers turn everything into generic classes and
       | functions which then makes it very difficult for anyone else to
       | figure out what is going on.
       | 
       | One reason I like go is it doesn't have all the bells and
       | whistles that give too many choices.
        
         | libria wrote:
         | At this point I'd like to summon to go-generics defense all the
         | PHP and Javascript developers who assert unwaveringly "Bad
         | language design doesn't cause bad code; bad programmers cause
         | bad code."
        
           | draw_down wrote:
           | Boy. You guys always manage to find interesting new ways to
           | hold PHP/JS devs in low regard. I will give you all that.
        
         | phamilton wrote:
         | I'd like generics for concurrency constructs. Obvious ones like
         | Mutex<T> but generics are necessary for a bunch of other
         | constructs like QueueConsumer<T> where I just provide a
         | function from T -> error and it will handle all the concurrent
         | consumption implementation for me. And yes, that's almost just
         | a chan T except for the timeouts and error handling and
         | concurrency level, etc.
        
         | qppo wrote:
         | Here's some generic code I wrote in Rust recently. I had two
         | unrelated iterators/collections and I needed to flatten and
         | sort them into a single buffer.                   struct Data {
         | idx: usize             // ...          }
         | struct Foo {             stuff: Vec<Vec<Data>>         }
         | struct Bar {             stuff: Vec<Data>         }
         | fn flatten_and_sort (foo:Foo, bar:Bar) -> Vec<Data> {
         | let mut output =                  foo.stuff.into_iter()
         | .flat_map(|v| v.into_iter())
         | .chain(bar.stuff.into_iter())
         | .collect::<Vec<_>>();                  output.sort_by(|lhs,
         | rhs| lhs.idx.cmp(&rhs.idx));             output         }
         | 
         | Now you could argue that this is just "generics for
         | collections" but the way those iterator combinators are written
         | make heavy use of generics/traits that aren't immediately
         | applicable for collections. Those same combinator techniques
         | can be applied to a whole host of other abstractions that allow
         | for that kind of user code, but it's only possible if the type
         | system empowers library authors to do it.
        
           | saghm wrote:
           | You can actually use the power of generics to get rid of the
           | two inner calls to `into_iter()`:                   let mut
           | output: Vec<_> =               foo.stuff.into_iter()
           | .flat_map(|v| v)                 .chain(bar.stuff)
           | .collect();
        
         | jasonhansel wrote:
         | I can think of a few other potential use cases in Go. Some
         | ideas:
         | 
         | - Promises
         | 
         | - Mutexes like Rust's Mutex<T> (would be much nicer than
         | sync.Mutex)
         | 
         | - swap functions, like swap(pointer to T, pointer to T)
         | 
         | - combinators and other higher-order functions
        
           | [deleted]
        
         | gowld wrote:
         | Mathematically, almost everything generic can be viewed as a
         | collection.
        
         | jdxcode wrote:
         | considering the vast majority of programming involves loops I
         | don't see "just for collections" as some minor thing--it's most
         | of what I do
        
         | jerf wrote:
         | I expect after a flurry of initial excitement, the community
         | will settle on some standards about what it is and is not good
         | for that will tend to resemble "Go 1.0 + a few things" moreso
         | than "A complete rewrite of everything ever done for Go to be
         | in some new 'generic' style".
        
         | catalogia wrote:
         | I agree, those grapes are probably sour anyway...
        
         | mixedCase wrote:
         | > which then makes it very difficult for anyone else to figure
         | out what is going on
         | 
         | Or we can learn to read them. Just treat types like a first
         | class value. You either assign names to types like you do to
         | values, or you can assign a name to a _function_ that returns a
         | type, this being generics.
        
           | systemvoltage wrote:
           | > or we can learn to read them
           | 
           | That's an awful way to think about hard to read code. I could
           | produce the most unreadable one liners you've ever seen in
           | your life. We should condemn that and not blame it on others
           | to "learn how to read".
        
             | gowld wrote:
             | I assume you only program in readable languages like COBOL
             | and AppleScript.
        
             | mixedCase wrote:
             | > That's an awful way to think about hard to read code
             | 
             | Most of the time I hear about "hard to read code" is
             | "pattern I don't currently have a mental model for". We
             | didn't move on from COBOL by letting that be a deterrant.
        
             | ragebol wrote:
             | You write code for an audience. In that audience, sit
             | yourself in your current state, yourself a year+ from now,
             | your colleagues (you know their level) and the compiler.
             | With bad luck, your current self i a state pulling your
             | hair out to debug.
             | 
             | Think about the audience when you code.
        
         | gokhan wrote:
         | What's wrong with this?                 class Result<T>       {
         | bool IsSuccess {get; set;}         string Message {get; set;}
         | T Data {get; set;}       }
         | 
         | On many occasions, I like using result types for defining a
         | standard response for calls. It's typed and success / fail can
         | be handled as a cross-cutting concern.
        
           | lenish wrote:
           | I'm not sure why you'd use a class like this in Go when you
           | have multiple returns and an error interface that already
           | handles this exact use case.
        
             | erik_seaberg wrote:
             | It's a lot cleaner to pass a Result<T> through a channel or
             | a slice than to create two channels or slices and confirm
             | everyone's following the same convention when using them.
        
               | lenish wrote:
               | I concede that there are probably scenarios where this
               | design makes sense within that context. I typically find
               | that either I care about a single error and terminating
               | the computation, or I don't care about errors at all. In
               | the former case, the primitives in the sync package (or
               | just an error channel which we send to once and close)
               | are adequate. The latter case presents no issues, of
               | course.
               | 
               | At $work we definitely have examples where we care about
               | preserving errors, and if that tool were implemented in
               | Go a solution like a Result struct containing an error
               | instance and a data type instance could make sense.
        
             | apta wrote:
             | Because multiple return values for handling errors is a
             | strictly inferior and error prone way for dealing with the
             | matter.
        
               | californical wrote:
               | It's interesting that you say this, because I've had the
               | opposite experience. I wouldn't say it's _strictly
               | inferior_ , because there are definitely upsides. If it
               | was strictly inferior, why would a modern language be
               | designed that way -- there must be some debate right?
               | 
               | I love multiple returns/errors. I find that I never
               | mistakenly forget to handle an error when the program
               | won't compile because I forgot about the second return
               | value.
               | 
               | I don't use go at work though, I use a language with lots
               | of throw'ing exceptions, and I regularly miss handling
               | exceptions that are hidden in dependencies. This isn't
               | the end of the world in our case, but I prefer to be more
               | explicit.
        
               | apta wrote:
               | > If it was strictly inferior, why would a modern
               | language be designed that way
               | 
               | golang is not a modern language (how old it is is
               | irrelevent), and the people who designed it did not have
               | a proper language design background (their other
               | accomplishments are a different matter).
               | 
               | Having worked on larger golang code bases, and I've seen
               | several times where errors are either ignored or
               | overwritten accidentally. It's just bad language design.
        
               | fooster wrote:
               | Error handling is hard, period. Error handling in go is
               | no worse than any other language, and in most ways it is
               | better being explicit and non-magic.
               | 
               | > people who designed it did not have a proper language
               | design background
               | 
               | Irrelevant.
               | 
               | > It's just bad language design.
               | 
               | try { ... } catch(Exception ex) { ... }
        
               | dgb23 wrote:
               | I cannot think of a language where errors cannot be
               | ignored. In go it is easy to ignore them, but they stick
               | out and can be marked by static analysis. The problems
               | you describe are not solved at the language level, but by
               | giving programmers enough time and incentives to write
               | durable code.
        
               | lenish wrote:
               | func foo() (*SomeType, error) {             ...
               | return someErr         }              ...         result,
               | err := foo()         if err != nil {             //
               | handle err         }         // handle result
               | 
               | vs                   type Result struct {             Err
               | error             Data SomeType         }
               | func (r *Result) HasError() bool {             return
               | r.Err != nil         }              func bar() *Result {
               | ...             return &Result { ... }         }
               | ...         result := bar()         if result.HasError()
               | {            // handle result.Err         }         //
               | handle result
               | 
               | I'm not really sure I see the benefit to the latter. In a
               | language with special operators and built-in types it may
               | be easier (e.g. foo()?.bar()?.commit()), but without
               | these language features I don't see how the Result<T>
               | approach is better.
        
               | et1337 wrote:
               | Go can't really express the Result<T> approach. In Go,
               | it's up to you to remember to check result.HasError(),
               | just like it's up to you to check if err != nil. If you
               | forget that check, you'll try to access the Data and get
               | a nil pointer exception.
               | 
               | The Result<T> approach prevents you from accessing Data
               | if you haven't handled the error, and it does so with a
               | compile-time error.
               | 
               | Even with Go's draconian unused variable rules, I and my
               | colleagues have been burned more than once by forgotten
               | error checks.
        
               | dgb23 wrote:
               | I would say it is a very ergonomic way of doing this. It
               | allows for writing in a more exploratory way until you
               | know what your error handling story is. Then, even if you
               | choose to propagate it later, you just add it to your
               | signature. Also it is very easy to grok and clear.
               | Definitely not strictly inferior.
        
           | AaronFriel wrote:
           | That's a generic container of 0 or 1 elements ;)
           | 
           | It's also incredibly unsafe and why generics aren't enough.
           | C++, Java, and so on have had generics for ages and with
           | types like the one above, null pointer exceptions are
           | incredibly common. Nothing prevents the user from attempting
           | to retrieve the data without first retrieving the success
           | status.
           | 
           | On the other hand, this improves on it dramatically:
           | enum Result<T, E> {           Success(T),
           | Failure(E)         }
        
             | masklinn wrote:
             | > That's a generic container of 0 or 1 elements ;)
             | 
             | Then chances are so are most if not all of the uses of
             | generics OP criticises. The only "non-container" generics I
             | can think of is session types where the generic parameter
             | represents a statically checked state.
        
             | jcelerier wrote:
             | > It's also incredibly unsafe and why generics aren't
             | enough. C++, Java, and so on have had generics for ages and
             | with types like the one above, null pointer exceptions are
             | incredibly common.
             | 
             | uh, you'd never get a null-pointer exception in C++ given
             | the type that OP mentioned. Value types in C++ cannot be
             | null (and most things are value types by a large margin).
        
               | temac wrote:
               | > Value types in C++ cannot be null
               | 
               | They can just not exist. And C++ being C++, dereferencing
               | an empty std::optional is UB. In practice this particular
               | UB _often_ leads to way worse consequences than more
               | "conventional" null-pointer derefs.
        
               | AaronFriel wrote:
               | Then you can't construct it unless it's successful, no?
               | 
               | A Result<T> that can only contain successful values
               | doesn't seem very useful
        
               | grandmczeb wrote:
               | Yes you can? The equivalent type in C++ is
               | std::expected[1] which doesn't even contain a pointer
               | that could be dereferenced (unless T is a pointer
               | obviously).
               | 
               | [1] unfortunately not standardized yet
               | https://github.com/TartanLlama/expected
        
               | AaronFriel wrote:
               | Who are you replying to? Is it in any way related to the
               | original comment I replied to and this type?
               | class Result<T>         {           bool IsSuccess {get;
               | set;}           string Message {get; set;}           T
               | Data {get; set;}         }
        
               | grandmczeb wrote:
               | I am replying to you and its pretty obviously related to
               | your comment.
               | 
               | You: "C++, Java, and so on have had generics for ages and
               | with types like the one above, null pointer exceptions
               | are incredibly common."
               | 
               | jcelerier: "you'd never get a null-pointer exception in
               | C++ given the type that OP mentioned."
               | 
               | You: "Then you can't construct it unless it's successful,
               | no?"
               | 
               | Me: "The equivalent type in C++ [to what the OP
               | mentioned] is std::expected". It is not possible to get a
               | null-pointer exception with this type and yet you can
               | construct it.
        
               | AaronFriel wrote:
               | It sounds quite a lot like you took the type the OP
               | posted and changed it in your reply to a different type
               | that isn't standardized yet, do I have that right?
        
               | grandmczeb wrote:
               | The code the OP posted is not C++. If you translate it to
               | C++ there is no way to get a null pointer exception.
               | 
               | It sounds quite a lot like you're looking to be
               | pointlessly argumentative, do I have that right?
        
               | [deleted]
        
               | AaronFriel wrote:
               | There are two things being discussed in this thread.
               | 
               | 1. The first, my original point was that a high quality
               | type system enforces correctness by more than just having
               | generics. There's no proper way in C++ to create this
               | class and make a sum type - there's no pattern matching
               | or type narrowing like can be done in languages with more
               | interesting type systems and language facilities.
               | Generics is just a first step to a much more interesting,
               | safer way of writing code.
               | 
               | 2. The second, my replies to folks who have corrected me,
               | and I'll borrow your little paraphrase here:
               | 
               | > You: "C++, Java, and so on have had generics for ages
               | and with types like the one above, null pointer
               | exceptions are incredibly common."
               | 
               | >
               | 
               | > jcelerier: "you'd never get a null-pointer exception in
               | C++ given the type that OP mentioned."
               | 
               | >
               | 
               | > You: "Then you can't construct it unless it's
               | successful, no?"
               | 
               | I think this is exactly correct still. If it's impossible
               | to create an instance of Result<T> without... well, a
               | successful result, you may as well just typedef Result<T>
               | to T, right? If it can't store the "failure" case, it's
               | totally uninteresting.
               | 
               | If it _can_ store the failure case, making it safe in C++
               | is fairly challenging and I dare say it will be a little
               | longer and a little less safe than a Result I can write
               | in a few lines of TypeScript, Scala, Rust, an ML or
               | Haskell derivative, and so on.
               | 
               | Now, I'd love to be proven wrong, I haven't written C++
               | for a while so the standard may have changed, but is
               | there a way to write a proper enum and pattern match on
               | the value?
               | 
               | It looks like this std::expected thing is neat, but can a
               | _regular person_ write it in their own code and expect it
               | to be safe? I can say with certainty that I can do that
               | for the languages I listed above and in fewer than 10
               | lines of code.
               | 
               | The C++ answer to that is, well, this:
               | 
               | https://github.com/TartanLlama/expected/blob/master/inclu
               | de/...
               | 
               | I don't think it's a comparison.
        
               | joshuamorton wrote:
               | No, you just are forced to use methods like
               | foo.UnwrapOr(default_value) to get the Result. Or
               | depending on the language, you get a compile error if you
               | don't handle both possible values of the Result enum in a
               | switch statement or if/else clause.
               | 
               | See for example https://doc.rust-
               | lang.org/std/result/enum.Result.html#method... in rust, h
               | ttps://docs.oracle.com/javase/8/docs/api/java/util/Option
               | al... in Java, and https://en.cppreference.com/w/cpp/util
               | ity/optional/value_or in C++.
        
               | AaronFriel wrote:
               | Who are you replying to? Is any of your elaboration
               | related to this result type?                   class
               | Result<T>         {           bool IsSuccess {get; set;}
               | string Message {get; set;}           T Data {get; set;}
               | }
        
               | joshuamorton wrote:
               | Ah you're quite correct.
        
               | nine_k wrote:
               | Well, T can be a pointer / reference here.
        
               | jcelerier wrote:
               | That wouldn't change anything to Result<T>'s implicit
               | safety properties. "safe + unsafe == unsafe" - to have a
               | meaningful discussion we should focus on the safe part,
               | else it's always possible to bring up the argument of
               | "but you can do ((char*)&whatever)[123] = 0x66;"
        
               | sedatk wrote:
               | You can also constrain a generic type only to value types
               | in C#:                 class Result<T> where T: struct
               | {       ...       }
               | 
               | In that case it can't be null with C# either.
        
               | AaronFriel wrote:
               | Then you can't construct it unless it's successful, no?
               | 
               | A Result<T> that can only contain successful values
               | doesn't seem very useful
        
               | loopz wrote:
               | Instead you can have undefined behaviour in C++.
               | 
               | Don't think get;set is C++, though it breaks
               | encapsulation.
        
             | polskibus wrote:
             | With c# 8 you have nullable references and you can use the
             | compiler to guard you against null pointer exceptions.
        
           | jasonhansel wrote:
           | Result types are much better than multiple return values. But
           | now the entire Go ecosystem has to migrate, if we want those
           | benefits (and we want consistent behavior across APIs). It'd
           | be like the Node.js move to promises, only worse...
        
           | londons_explore wrote:
           | I feel like such a class should either be part of the
           | language, and part of language idioms etc, or it shouldn't be
           | used.
        
             | tomjen3 wrote:
             | Can you articulate why? it seems to me that 'feel' should
             | not be part of the discussion.
        
               | [deleted]
        
               | lenish wrote:
               | Not GP, but I've sometimes found libraries implementing
               | similar concepts differently causing issues.
               | 
               | E.g.                   libraryA.Result struct {
               | Err error             Data SomeDataType         }
               | libraryB.Result struct {             err string
               | Data SomeDataType         }         func (r
               | libraryB.Result) Error() string {              return
               | r.err         }
               | 
               | Now you have two different implementations of the same
               | fundamental idea, but they each require different
               | handling. In Go, where many things simply return an error
               | type in addition to whatever value(s), you would now have
               | three different approaches to error handling to deal with
               | as opposed to just whatever the language specified as the
               | best practice.
        
               | juststeve wrote:
               | Not GP but:
               | 
               | It may frustrate coworkers who need to edit the code.
               | 
               | It adds another dependency into your workflow.
        
         | ragebol wrote:
         | If you don't understand someone else's code, you can either
         | tell them they stuff is too complicated or learn and understand
         | better. There can be a middle ground of course.
        
           | logicslave wrote:
           | Most of the time if code is hard to understand its bad code.
           | Just because someone writes complex code that uses all the
           | abstractions, doesnt mean its good. Usually it means the
           | opposite
        
         | simias wrote:
         | I've certainly encountered codebases that abused inheritance
         | and templates/generics to the point of obfuscation but you can
         | abuse anything really. Besides in my experience the worst
         | offenders where in C++ where the meta-programming is
         | effectively duck-typed. Trait-based generics like in Rust go a
         | long way into making generic code readable since you're always
         | aware of what meta-type you're working with exactly.
         | 
         | I definitely don't use generics if they can be avoided, and I
         | think preemptive use of genericity "just in case" can lead to
         | the situation you describe. If I'm not sure I'll really need
         | generics I just start writing my code without them and refactor
         | later on if I find that I actually need them.
         | 
         | But even if you only really care about generics for
         | collections, that's still a massive use case. There's a wealth
         | of custom and optimized collections implemented in third party
         | crates in Rust. Generics make these third-party collections as
         | easy and clean to use as first party ones (which are usually
         | themselves implemented in pure Rust, with hardly any compiler
         | magic). Being easily able to implement a generic Atomic type, a
         | generic Mutex type etc... without compiler magic is pretty damn
         | useful IMO.
        
       | kodablah wrote:
       | This is going to give rise to a utility-belt of sorts, which is
       | what I'm really looking forward to. There are many collections
       | and common utilities that we've all been hand-writing for a while
       | (e.g. waiting for Set(string) instead of map[string]struct{}, or
       | a TreeMap(string, Thing) instead of maintaining sort order
       | separate or using reflection for an existing lib).
       | 
       | > Methods may not take additional type arguments
       | 
       | While I can fathom the complications that arise, I'm hoping they
       | can work past this restriction by launch.
        
       | albeva wrote:
       | I feel like Go lost its chance by being way too slow to innovate.
       | From my circle of people who I remember were extremely excited
       | about Go when it first came out - no one really uses it anymore.
       | In our circle it has become a "meh" language. Heck Apple Swift on
       | server is way more exciting than Go these days.
        
         | zerotolerance wrote:
         | It isn't supposed to be an exciting language. It specifically
         | not fancy. Great for teams. Forced style consistency. Clear
         | expectations. Difficult to mis-read compared to most others.
         | Terse, almost rude.
        
           | enahs-sf wrote:
           | There is a lot of beauty in go's simplicity. Very
           | straightforward to reason about what's going on.
           | 
           | I find it much easier on the eyes than java.
        
         | thomascgalvin wrote:
         | Go is huge in the Kubernetes world... mainly because both are
         | Google inventions. But almost all of the third-party
         | integration I've seen in k8s has involved Go.
        
           | majewsky wrote:
           | Kubernetes actually started out in Java, and was only
           | rewritten into Go later (before 1.0 though). The choice of Go
           | could also have been influenced by Docker being written in Go
           | (which also meant that some Docker-related libraries were
           | already available for Kubernetes to use).
        
             | sagichmal wrote:
             | The super early Java prototype of Kubernetes lived for
             | about a week or two and never saw a public repository. It's
             | kind of misleading to say it "started out in Java".
        
         | hunterloftis wrote:
         | I have not seen any data to indicate that server-side
         | developers (outside of your circle) are more excited about
         | Swift than Go.
        
           | iphone_elegance wrote:
           | yeah for me personally it's tricky, I like Swift as a
           | language but I'm pretty happy with rust and c++ for cross-
           | platform system/graphics work (there are a ton of great
           | libraries) and there's a million options for network services
           | including Go that feel more built up
           | 
           | i feel like i'm at a point of language overload as it is
           | these days, I don't have the mental space for yet another
           | language
        
         | Sachaniman wrote:
         | When I first encountered Go in 2015, it was exciting because it
         | made concurrent programming accessible and easy compared to the
         | competition. Fast forward to 2020, and many languages have
         | either caught up or improved on the advances Go made (e.g.
         | Kotlin, Rust, Elixir, Scala, etc.). That being said, I think
         | you might be in a bubble if you don't know anyone using Go.
         | It's everywhere nowadays, for better or worse.
         | 
         | I agree with you that I think they're slow to improve. I'm not
         | sure if innovation is necessary though, since most people rely
         | on the language being stable going forward.
         | 
         | Personally I struggle with deciding when to use Go for a new
         | project. With so many languages now supporting different
         | concurrency paradigms, and containers abstracting away prior
         | deployment pain points, I'm not sure why I'd reach for Go over
         | any of the others.
        
         | grey-area wrote:
         | Exciting is not a criteria you should use in choosing a
         | programming language. Go is boring, and that's a good thing for
         | those using it, because they can get on with their real work
         | without worrying about the language changing.
         | 
         | I'm pleased to see they've made this proposal even more boring
         | and removed contracts.
        
         | matwood wrote:
         | I wonder if you don't hear about it because it 'just works.'
         | That's been my experience with Go. No frills, get stuff done
         | type of language. No language is perfect, but Go doesn't have
         | leftPad issues or a new language construct weekly like JS. It
         | also doesn't have decades of baggage to lug around like Java.
         | There just isn't much for people to talk about. It's a boring
         | language that is, for the most part, easy to read and write.
        
           | bubba1236 wrote:
           | NPM is 1000x better than the lack of a package manager in go.
        
       | adamch wrote:
       | Finally. Better late than never. I have to write a lot of Go code
       | at $BIGCORP and keep reaching for generic helper functions like
       | Keys or Map or Filter or Contains. This is going to make my code
       | much more readable.
        
         | nemo1618 wrote:
         | Will it really?
         | 
         | Old:                 sum := 0       for _, x := range xs {
         | sum += xs       }
         | 
         | New:                 slices.Reduce(xs, 0, func(i, j int) int {
         | return i + j       })
        
           | hombre_fatal wrote:
           | That's the most compelling example you could come up with as
           | someone who presumably writes Go?
           | 
           | Reversing a slice of T. Copy and pasting this:
           | for i := len(a)/2 - 1; i >= 0; i-- {             opp :=
           | len(a) - 1 - i             a[i], a[opp] = a[opp], a[i]
           | }
           | 
           | New:                   reverse(a)
           | 
           | Or generic versions of these:
           | https://gobyexample.com/collection-functions
        
           | TheCoelacanth wrote:
           | New:                   slices.Sum()
        
           | dilap wrote:
           | Yeah no real gain for that case, but for something like
           | filter, definitely a win.
        
       ___________________________________________________________________
       (page generated 2020-06-16 23:00 UTC)