[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)