60 ms·
Go 2 Draft Designs
- atombender 8y agoI'd like to see sum types (often called product types, union types or enums) introduced. I think they would fit Go well, and get us away from the current awkward "interface hell". For example, imagine you're implementing an AST. You might have something like: type Identifier struct { Name string } type StringLiteral struct { Value string } and so on. Currently the only way to unify them is with a dummy interface: type Expression interface { isExpression() } and then you have: type (Identifier) isExpression() type (StringLiteral) isExpression() // etc. Then in your compiler you'll have lot of switch statements: switch e := expr.(type) { case Identifier: // ... case StringLiteral: // ... default: panic("unexpected expression") } There are multiple problems here. One, unifying pure data structs with an interface is not what Go interfaces were intended for, and is pure type system abuse. Fine, so maybe in this case Expression also gets things like GetPos() and what not, which sort of justifies an interface. But there are plenty of use cases where there are no methods. The other problem is that there's no way to prove at compile-time that a switch is exhaustive. Add another expression struct, and all your switches still compile. Someone made a post-processing tool (go-sumtype), but it's an awkward and buggy hack that has to be run as a "go generate" task next to your development workflow. Thirdly, performance-wise, this forces every struct to be boxed in an interface. I'd rather like to be able to declare something like: type Expression (Identifier || StringLiteral) It can, I suspect, be simpler than Rust's full-blown sum type support, because you're essentially declaring a new type that holds the possible union of types. Of course, this could be elegantly extended to errors: func Open(fn string) (*File || error) The downside to this, as opposed to something like Rust's Result, is that you can't add methods, so you can't chain these calls without something more. On the other hand, it would work together with the proposed check/handle mechanism.
- Attained 8y agoReally wish they'd consider looking at some of the more fun things TypeScript types can do. Like specifying a return type as being Pick<SomeType, 'x' | 'y'>. Imagine this in combination with Go interfaces.
- chmike 8y agoWhat about literal parameter for functions or types ? I'm just asking because c++ and D support it.
- ianlancetaylor 8y agoOne step at a time. It's certainly a natural extension, and I don't think anything in the current design draft precludes it.
- conroy 8y agoOld and busted: func printSum(a, b string) error { x, err := strconv.Atoi(a) if err != nil { return err } y, err := strconv.Atoi(b) if err != nil { return err } fmt.Println("result:", x + y) return nil } New hotness: func printSum(a, b string) error { x := check strconv.Atoi(a) y := check strconv.Atoi(b) fmt.Println("result:", x + y) return nil }
- spyspy 8y agoOnly seems useful when you have a function and want every error handled the exact same way, and don't have any requirement to add context the way https://github.com/pkg/errors https://github.com/pkg/errors does.
- ianlancetaylor 8y agoRead the design draft (https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md https://go.googlesource.com/proposal/+/master/design/go2draf...). The issues you raise are addressed.
- spyspy 8y agoNot quite. You'd need to write new `handle err { ... }` blocks for every new context, like how they have one in the for loop. In effect you're just moving the handling of the error away from the place it occurred, which is not optimal. As someone who writes Go every day I don't see this strategy effectively cutting down on boilerplate core or adding clarity.
- tracker1 8y agoWhile this is true, more often than not, in lower-level functions you want errors to bubble up. For example, in most Node.js code, the first line in each callback raises the error to the callback, or likewise throws up in a promise/async chain. Beyond this, there's no reason you can't add additional context and/or wrap the original error before returning your own error. With this additional syntax and the addition of generics, I'm far more likely to take up go. Up to this point, I'd only toyed with it a little, because it just felt so cumbersome to actually get stuff done compared to node. Beyond that, dealing with web-ui, if I'm going to take on a cognitive disconnect for a back-end, I want as little friction as possible.
- aleksi 8y agoPlus a short video in the Go blog: https://blog.golang.org/go2draft https://blog.golang.org/go2draft
- s_kilk 8y agoIt'll be interesting to see how Go handles a new major version. Will it be a Python 3 situation? Or more like, I dunno, Ruby 2 and onwards.
- kyrra 8y agoWith these proposals so far, nothing is breaking with existing code. Any code you right today that runs on Go 1.11, would compile and run with these proposals. They are just feature additions at this point (mainly some new keywords, like "contract", "handle", and "check").
- SamWhited 8y agoI don't know how Ruby 2 does things, but Go will definitely not be like Python 2/3. Old Go 1 programs will continue to compile under Go 2, but Go 2 programs will not compile under Go 1. It's also not certain that we will need a Go 2. It may be that all features can be written in a backwards compatible manner and the major version won't need to be bumped.
- yashap 8y agoI think the two big problems with the Python 3 rollout were a lack of compelling reasons to upgrade (at the time - many big features have been implemented in Python 2 in the years since), and upgrades like this being inherently scarier in dynamically types languages. If Go 2 brings improved error handling and generics, those are HUGE reasons for the community to get onboard and upgrade quickly. Plus with compiled languages, the upgrade is way less scary - so many errors will be caught at compile time that can sneak through as subtle bugs in dynamically types languages (unless there’s GREAT test coverage). Also, Go 2 may not even bring breaking changes! Though I’d imagine some things will still break no matter what (code analysis tools, programs that use variable names that become keywords, etc.). Maybe I’m being naive, but I think Go 2 will be adopted quickly with relatively little pain, as long as it brings super-desired features like genetics and error handling improvements.
- reggieband 8y ago> I think the two big problems with the Python 3 rollout When Python 3 was announced I thought "this is a great way to rollout". After a few years I thought "what a terrible rollout, even I haven't changed to version 3 100% yet because of some libraries I rely on are still only verion 2". Now I'm back on the positive side of things. It turns out, in the grand scheme of things, 5+ years for a new version to be adopted didn't really affect me in a negative way. I'm back on the "slow and steady wins the race" bandwagon.
- pcwalton 8y agoLooks good. The control flow of "handle" looks a bit confusing, but overall I definitely like the direction this is going. The decision to go with concepts is interesting. It's more moving parts than I would have expected from a language like Go, which places a high value on minimalism. I would have expected concepts/typeclasses would be entirely left out (at least at first), with common functions like hashing, equality, etc. just defined as universals (i.e. something like "func Equal<T>(a T, b T) bool"), like OCaml does. This design would give programmers the ability to write common generic collections (trees, hash tables) and array functions like map and reduce, with minimal added complexity. I'm not saying that the decision to introduce typeclasses is bad—concepts/typeclasses may be inevitable anyway—it's just a bit surprising.
- the_duke 8y agoI don't like the possibility of nested handle() blocks. This could make grasping the control flow in a function very cumbersome. Everything else looks like a very good change to me and would draw me back in to Go.
- pcwalton 8y agoYeah, the control flow of "handle" is the most questionable thing, from my standpoint. (Incidentally, I find the function-scoped nature of "defer" unfortunate too.)
- kenhwang 8y agohandle/check looks like try/catch in reverse order with check/try limited to statements instead of code blocks. Not sure if that's an improvement or just different for the sake of being Go.
- timClicks 8y agoI have noticed that my Python code increasingly works like this via the optional else clause in its try/except mechanism.
- lostmyoldone 8y agoThat error handling looks very much FP inspired, intended or not. I'm not going to say the M-word, someone will maybe say it in ... either case.
- superdimwit 8y agomonad transformer stacks amiriiiiiiiite
- lostmyoldone 8y agoI didn't have a very precise pattern in mind, but yes that would smell about right!
- wisam 8y agoI'm not going to say the M-word, someone will *maybe* say it in ... *either* case. Just wanted to say I see what you did there. Nothing insightful to add.
- lostmyoldone 8y agoHad a bit of a rough day, and insightful or not, your comment still did brighten my day.
- spullara 8y agoI really thought they were going to allow their idealism to win over pragmatism. Versioned modules, generics and exception handlers all going in is really going to put the defenders of them being missing in a tough spot.
- the_duke 8y agoEdit: To clarify, the handle() system does indeed resemble an exception propagation system and is also the part I like least. Just "check" would be preferable, with an implicit handler of "if err != nil { return err }" Although I still would prefer something leaner like the "?" operator from Rust.
- quotemstr 8y ago> The draft design introduces two new syntactic forms. First, it introduces a checked expression check f(x, y, z) or check err, marking an explicit error check. Second, it introduces a handle statement defining an error handler. When an error check fails, it transfers control to the innermost handler, which transfers control to the next handler above it, and so on, until a handler executes a return statement. Sure. It's explicit error handling that just so happens to look exactly like a form of exception handling, c.f., the zero-overhead exceptions proposal in C++land. This is how you say, in language-design-ese, "I want to do what my critics have been saying for years is the right thing without admitting that they've been right all along".
- heavenlyhash 8y agoI really don't know where people get on this high horse and imagine that there's been some kind of fighting attitude here. I saw a talk by James Gosling (of Java fame) a few months ago in which he said one of the most important things he's glad he did throughout the development of the language is "Decide... not to decide". The purpose of this "deciding not to decide" should sound familiar by now: once you commit to something, it's hard to backtrack. And once it's hard to backtrack, you have lost your ability to look for more optimal solutions than the one you've fixated on. This is why generics in Java took so long. And to this day, the developers of those generics do not regret taking their time to do so. I think that's interesting to note, don't you? It's okay to make decisions after deliberation. Deliberation does not imply a fight. --- Tone police aside, did you... read... the proposals? I don't think it's correct to dismissively say that the proposed handle/check syntax is identical to other checked exception syntaxes; it doesn't compose in the same way as traditional try-catch blocks; and it doesn't have the power to create arbitrary longjumps across multiple stack frames as exceptions do. In short, it's very little like traditional exceptions at all.
- tmaly 8y ago> The call with type arguments can be omitted, leaving only the call with values, when the necessary type arguments can be inferred from the value I think if too many different ways are offered in the generics proposal, it will work against the simplicity of the language for newcomers.
- TACIXAT 8y agoYea, I really like Go because it isn't C++2049. I was able to get writing it quickly, and I don't have to think too hard about what the right (read: most clever) way to implement something is given all the language features. I like to just write code and I think a post here said it best, Go gets out of the way and lets you build.
- scns 8y agoweeks of work can save an hour of planning
- platinumrad 8y agoNot having to provide the time argument at the callsite is by far the most intuitive way to call a generic function.
- kodablah 8y agoI was hoping the generic type would be part of the struct/interface/func declaration section instead of the name part of the declaration. I wonder how that will work with anon ifaces/structs/funcs? I assume just a type param list before the anon decl? (seems easy with funcs, less so w/ types). Also, I assume I can make named aliases of types with concrete type params values?
- sseth 8y agoThere are examples of aliases e.g. type VectorInt = Vector(int)
- djhworld 8y agoReally excited to see things start to take shape, I'd imagine there will be a lot of rounds in that feedback loop to get it right, but glad to see the Go team have stuff on the roadmap
- teolandon 8y agoI do like the idea of contracts for generics, seems to fit in well with Go's interfaces and whole design. What I do not like at all is that type arguments are put in parens, same as normal arguments. Something like Java's <T> would be more readable IMO. I have to do a double-take to realize that the first paren is for the type arguments. Combine that with the parens that are used for receivers and multiple return values, and function signatures become paren hell.
- platinumrad 8y ago<T> has issues with ambiguous parsing as < and > are also the less than and greater than operators. Rust recently had an small issue with this: https://github.com/rust-lang/rust/pull/53562 https://github.com/rust-lang/rust/pull/53562
- edflsafoiewq 8y ago[T] has precedents (Eiffel comes to mind).
- kbd 8y agoI'm always so happy to see Eiffel mentioned. I read Object Oriented Software Construction immediately before I read the book on (then recently-released) Java 1.2, and it made it so clear how much Java got wrong :(
- ianlancetaylor 8y agoUnfortunately [T] is also ambiguous with the current Go syntax. The parser can't distinguish an array declaration from a generic type.
- alecthomas 8y agoHow about the following: contract Addable(t T) { t + t } func Sum.<T Addable>(x []T) T { var total T for _, v := range x { total += v } return total } It has the nice property that it's somewhat analogous to type conversions, the grammar should be unambiguous, and it's much less visually confusing than re-using parentheses IMO.
- quotemstr 8y agoFWIW, these changes make it much more likely that I would voluntarily choose to write a Go program.
- felipeccastro 8y agoI agree, honestly the error handling alone has always been the main reason I never used Go. Yes, I knew there were good reasons for that design, but I prefer to have options in how to handle errors and keep a clean, elegant code.
- rdsubhas 8y agoTo be frank, I was for a long time on the camp that Generics is a much-needed feature of Go. Then, this post happened: https://news.ycombinator.com/item?id=17294548 https://news.ycombinator.com/item?id=17294548. The author made a proof-of-concept language "fo" on top of go with generics support. I was immediately thrilled. Credit to the author, it was a great effort. But then, after seeing the examples, e.g. https://github.com/albrow/fo/tree/master/examples/ https://github.com/albrow/fo/tree/master/examples/, I could see the picture of what the current codebase would become after its introduced. Lots of abstract classes such as "Processor", "Context", "Box", "Request" and so on, with no real meaning. Few months back, I tried to raise a PR to docker. It was a big codebase and I was new to Golang, but I was up and writing code in an hour. Compared to a half-as-useful Java codebase where I have to carry a dictionary first to learn 200 new english words for complicated abstractions and spend a month to get "assimilated", this was absolute bliss. But yes, inside the codebase, having to write a copy-pasted function to filter an array was definitely annoying. One cannot have both I guess? I believe Golang has two different kinds of productivity. Productivity within the project goes up quite a lot with generics. And then, everyone tends to immediately treat every problem as "let's create a mini language to solve this problem". The second type of productivity - which is getting more and more important - is across projects and codebases. And that gets thrown out of the window with generics. I don't know whether generics is a good idea anymore. If there was an option to undo my vote in the survey...
- jadbox 8y agoAll I want is typesafe efficient higher order list operations like map, filter, reduce, flatmap, zip, takeUntil, etc. I don't care if Go puts them into the stdlib and only allows these operators to use special internal generics to operate. If Go had a robust higher order std library like this, I would stop asking for generics. Again, must be typesafe and zero overhead.
- bradfitz 8y agoOne of the main motivations for adding generics is that we could then put typesafe algorithms and data structures in the standard library.
- JelteF 8y agoOverall really great that Go is addressing it's current biggest issues. I do think the argument for the check keyword instead of a ? operator like in Rust is quite weak. Mainly because a big advantage of the ? operator (apart from the brevity) is that it can be used in method calling chains. With the check keyword this is not possible. AFAICT the check keyword could be replaced for the ? operator in the current proposal to get this advantage, even while keeping the handle keyword. Furthermore the following statement about rust error handling is simply not true: > But Rust has no equivalent of handle: the convenience of the ? operator comes with the likely omission of proper handling. That's because the ? operator does a min more than the code snippet in the draft design shows: if result.err != nil { return result.err } use(result.value) Instead it does the following: if result.err != nil { return ResultErrorType.from(result.err) } use(result.value) This means that a very common way to handle errors in Rust is to define your own Error type consumes other errors and adds context to them.
- epage 8y ago> But Rust has no equivalent of handle: the convenience of the ? operator comes with the likely omission of proper handling. Additionally, Rust programs heavily use "RAII", avoiding the need for `handle`. But there is talk of adding support for `catch`. See https://internals.rust-lang.org/t/pre-rfc-catching-functions/6505 https://internals.rust-lang.org/t/pre-rfc-catching-functions...
- kyrra 8y agoI get the feeling that the core Go team is rather against 1-character operators. Also, the Go community doesn't tend to do a lot of call chaining from a general stylistic standpoint.
- zlynx 8y agoI've written a lot of Go in the last few years. I stopped chaining calls after I realized how much of a pain it is to debug. When you need to print / log a value from the middle of your call chain you have to take the chain apart. So just write it out to start with. It's not any less efficient.
- ilovecaching 8y agoI don't understand why promoting Go to be the next Java is a good thing. Let's just use it to do what it's good at, and if you need generics, go use a language that has generics. We already know what a language that can do everything looks like, and it's a mess. Plus, are they going to rewrite the entire STL? Are we going to get a library of Algos and DS when we have generics? Generics pretty much changes the entire look and feel of the language. Might as well make it a fork than an iteration. So much for simplicity and copying. :) RIP Go 2009-2019
- tree_of_item 8y agoWhat a strawman. Adding a basic PL feature means we're gonna rewrite the STL? Come on.
- jagger27 8y agoThe Go team is pretty set on maintaining backwards compatibility—rewriting the standard library is the exact opposite of that. Just because the promise is Go 1.X won't break your code doesn't mean they don't remember Python 3. Besides, which parts of the existing standard library would even benefit from generics themselves? Of course the obvious thing is to /add/ a set of standard container templates but that doesn't really step on anyone's toes.
- etse 8y agoIf you prefer Go as is, stay on 1.x? Besides, isn't v2 of a language like a fork anyway? There's a considerable investment in v1 already so it's not like it's going to disappear overnight.
- wvenable 8y agoI'm disappointed the Java has given exceptions such a bad wrap that all new programming languages dance around them like crazy. I understand Rust going with error returns due to it's low-level nature but Go is pretty high level. This semi-explicit error handling code optimizes for fairly trivial cases as very few real-world functions don't have some kind of exception condition. The difference between prefixing nearly every function with "check" vs. having that implicit is very small. Proper use of exceptions puts the focus on where you can handle the error rather than where the error occurs. Java screws this up with checked exceptions, which puts the all focus on error site, and programmers have subsequently determined (correctly) that checked exceptions are hardly better than error returns. However, that is fundamentally the wrong way to look at error management. If a method 17 levels deep on the call stack throws a network error, I shouldn't have to care about those 17 levels because I'm going to restart the whole operation at the point I can do that. The important part is not where the error is raised/returned.
- alkonaut 8y agoExceptions are second only to proper return types for error handling such as Result<T, E> or Option<T>. And in order to have those you have to have generics. Once you have generics result types can be used for 99% if error handling.
- hota_mazi 8y agoNo, because you are still bubbling these 17 stack frames, as OP was (rightfully) complaining about. The only downside of exceptions is the loss of equational reasoning, really. But for everything else, they are a superior approach to result values.
- mike_hearn 8y agoIt's not really about Java giving exceptions a bad rap. After all exceptions are a feature of almost any OOP language designed after 1990 and they work nearly the same way in all of them. I've argued in the past that the reason the current crop of languages that compile to native code tend to avoid exceptions, is to do primarily with implementation cost, the backgrounds of the language designers and a mistaken attempt at over-generalisation in an attempt to find competitive advantage. https://blog.plan99.net/what-s-wrong-with-exceptions-nothing-cee2ed0616 https://blog.plan99.net/what-s-wrong-with-exceptions-nothing... One problem is that the sort of people who write native toolchains that compile to LLVM or direct to machine code like Go, tend to be the sort of people who spent their careers working in C++ at companies that ban C++ exceptions. So they don't really care or miss them much. Another is complexity. One of the most useful features of exceptions is the captured stack trace. But implementing this well is hard, because generating a good stack trace in the presence of optimised code with heavy use of inlining requires complex deoptimisation metadata and many table lookups to convert the optimised stack back into something the programmer will recognise. Deopt metadata is something the compiler needs to produce as it works, at large cost to the compiler internals, but most compiler toolchains that aren't optimising JIT compilers (like the JVM) don't produce such metadata at all. So stack traces in natively compiled programs are frequently useless unless you compiled in "debug mode" where all optimisations are disabled. VMs like the JVM have custom compilers that generate sufficient metadata, partly because they have more use for it - they speculate as they compile and use the metadata to fall back to slow paths if their speculations turn out to be wrong. But a language like Go or Rust doesn't have a runtime that does this. Finally, I find many of the arguments cited against exceptions to be very poor. For example the Go designers cite Raymond Chen's blog posts as evidence that exceptions are bad because it's easy to forget to handle errors and the checks are "implicit". This seems totally backwards to me. You cannot forget to handle an exception in most languages that use them. If you forget a method can throw and don't catch it, the runtime will catch it for you and print out a stack trace and error message that tells you exactly what went wrong and where. The thread won't continue past the error point and the runtime will stop the thread or entire app for you. But if you forget to check an error code, the error is silently swallowed and no diagnostics are available at all. The program will continue blindly in what is quite possibly a corrupted state. The Go designers argue that with exceptions you can't see "implicit checks". This is an odd argument because, beyond not being able to see if you are ignoring a return code, all programs must have implicit checks of correctness. For example checking for null pointer dereferences or divide by zero conditions, yet it makes no sense for e.g. divide to be a function that returns an error code you must check every time. In C, such checks turn into signals that can be caught or on Windows, the OS delivers them as exceptions! Yes, even for C programs, it's called SEH and is a part of the OS API - all programs can have exceptions delivered to them at any point. If you don't catch them then the OS will catch it for you and display the familiar crash window. UNIX uses signals which are not as flexible but have similar semantics; if you don't catch the signal you'll get the OS default crash handler. So this is no knock against exceptions. Moreover, very often you don't want to handle an error. Not all code can or should attempt to handle every single thing that can go wrong at the call site, or even at all. So in practice errors are almost always propagated back up the stack, often quite far up. For many errors there's nothing you can do beyond the default exception handling behaviour anyway of printing an error or displaying a crash dialog and quitting, e.g. many programs can't handle out of memory or out of disk conditions and nor would it make sense to invest the time in making them handle those conditions. Exceptions were developed for good reasons; they were designed in response to the many enormous problems C-style error handling created and codified common patterns, like propagation of an error to the point where it makes sense to capture it, changing the abstraction level of errors and so on. The Go designers have realised what many people told them from the start - that their error handling approach is bad. Unfortunately they don't seem to have taken the hint and reflected on why they made these bad decisions in the first place. Instead their language comparison ignores all the other languages that went a different direction, and only looks at Swift and Rust. Neither language is especially popular. Other modern languages like Kotlin which do use exceptions are completely ignored. In conclusion, nothing in this document makes me think that Go is likely to fix its self-admitted design errors. They're probably just going to make variants of the same mistakes.
- deleted 8y ago[deleted]
- valarauca1 8y agoGenerics are just syntax sugar around `interface{}` [1] well hopefully the runtime JIT optimizes things for me. I'm rather impressed that when `go` actually implements something I wanted they still manage to implement it in a way that is hacky and has massive trade-off's while literally avoiding doing the bare minimum to improve the compiler. Its disappointing. [1] https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md https://go.googlesource.com/proposal/+/master/design/go2draf... go to efficiency
- nireldrogata 8y agoHace un año intenté adelgazar haciendo mucho deporte. Un entrenador que tuve me dijo que tomara una pastilla de cafeina para entrenar. – tómate 3, que te veo en baja forma, y ya verás que bien entrenaremos… – 3? no es mucho? – hazme caso, que ya verás que bien Le hice caso. Y fue una absoluta locura. No entrenaba, volaba. La hora y media que duró el entrenamiento se me quedó corto y cuando salí de alli, queria mas. Tenia a la tarde una escena programada con una actriz. Le meti un casquete de una hora que salió volando la tia. Yo estaba que lo flipaba. Esa escena me puse encima casi todo el rato jaja. Despues de esa chica, al cabo de un rato me llamó otra que a ver si podia rodar con ella, que necesitaba dinero bla bla. En circunstancias normales le hubiese dicho que no, con una basta al dia. Le dije que si. Asi que venga, otro polvete. Estuvimos una hora y media dale que te pego. Yo me sentia de maravilla, y podia con mas. A la noche llamé a una follamiga que tenia, y le meti otro cohete que duró casi dos horas. Que me estaba pasando doctor?. Tres en una dia. No sabia lo que me pasaba, pero creo que tenia mucho que ver esas pastillitas. Me costó mucho dormirme, asi que para relajarme me hice una paja. A eso de las 6 de la mañana conseguí dormirme. Al dia siguiente me levanté como si me hubieran puesto una puerta de cemento encima. Un dolor de cabeza antologico y estaba cansadisimo. Accion, reaccion. Eso es lo que pasa cuando te excedes un dia a causa de las drogas, que al final el cuerpo te pasa factura. Aprendi la leccion y nunca mas he tomado ni una sola pastillita. Lo natural es mucho mejor. Tardé dos-tres dias en recuperarme totalmente. Menuda paliza le di a mi cuerpo ese dia.
- deleted 8y ago[deleted]
- sremani 8y ago^ The above post is Spam and has absolutely nothing to do with topic of discussion. Overlords, please flag the post.
- rubenpelopolla 8y agoUnos doritos para abrir el estomago nunca vienen mal. Luego unas palmeritas de chocolate para rendir bien en el gym y para ganar musculo y por ultimo unas mazorcas de maiz en el vestuario.
- pjmlp 8y ago> In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier. Yep! The overall design looks quite good, though.
- dagss 8y agoI am disappointed that go seems to follow Java and C++ with generics support instead of taking a more LISP-like turn like e.g. Julia. I would much rather have powerful hygienic compile-time macros to generate code. Then I would have the power to do all thay generics does but also a lot more. Just having those powers at compile time evaluation would be rather good, don't need to go all the way to Julia/Lisp. We know what mess C++ became, with people abusing the template system to do compile-time metaprogramming. Much better to simply provide a good macro system.
- gameswithgo 8y agogenerics are immune to much of the mess that templates cause. It is a somewhat different thing. They are also less powerful, but that is fitting with Go's desire to avoid language constructs which let people make really confusing code, I think. If you want cool meta programming abilities, there is Rust, Nim, Lisp, Julia, F# and others to choose from. No need for Go.
- dagss 8y agoBy that logic, then currently, if you want cool generics abilities, there is Java for you. No need for Go. But then there's this spec for generics, so obviously one wants to develop Go further. And I think it's a valid question why generics in particular is chosen instead of other ways of solving the same problem.
- gameswithgo 8y ago>By that logic, then currently, if you want cool generics abilities, there is Java for you. No need for Go. Java has one of the least good generics implementations of modern languages. I suggest C#, or even better, F# if you want to make heavy use of them =) Generics compared to templates keep the language simpler, solve the most common cases of code repetition, and can easily provide good error messages and runtime performance.
- thethirdone 8y ago
- kanishkdudeja 8y agoThis made my day. I'm so happy. Going to spend the week going over the draft proposals!
- stephen 8y agoWell, shoot. I don't like/don't want to like Go, but they keep getting better. My general annoyance is that they keep things ~just different enough to be irksome, e.g. in this proposal generics is using parens: type List(type T) []T Instead of using <> like every other language (okay, I meant Java and C++) with generics: type List<type T> []T (Another example of being ~just different enough to be annoying was switching from C style `int i` to `i int` but then not using colons like `i: int`. And then using type name case for public/private (wtf), so I read variable decls of `foo foo`, which happens all the time in methods that are private implementation details, and my polygot brain interrupts: ...damn, which one is the type again?) As usual, I'm sure `(type T)` vs `<type T>` will be due to "reasons" (...oh, maybe it's that crazy `<-chan` syntax that I dyslexically can never keep straight and wish it was just real type names like `ReadChan[T]` and `WriteChan[T]`), but really I hop languages all day and this makes a big difference to my eyes going back/forth. They should also go back and fix `map[K]V` to be `Map[K, V]`; iirc one of the touted benefits of `gofmt` was to "make automated source code analysis/fixing easy". Great, take advantage of that and get rid of that inconsistency. Same thing with `Chan[string]` instead of `chan string` (both are public types so should be upper case for consistency). It's a major release, just do it. Anyway, disclaimer yeah I'm nit picking and this is what made Guido quit Python. To their credit, Go has great tooling, the go mod in 1.11 finally looks sane, etc. Their continual improvement is impressive.
- munificent 8y ago> Instead of using <> like every other language (okay, I meant Java and C++) with generics Using angle brackets for generics is really annoying when it comes to the grammar of the language. There are nasty edge cases around things like interpreting `>>` as a shift operator in some places but closing two type argument lists in others. There are occasional ambiguities like: foo ( a < b , c > - d) Is this "foo(a < b, c > -d)" or "foo(a<b,c> - d)"? Angle brackets are (alas) more familiar, but they aren't simple. Go seems to place a premium on the latter over the former.
- zemo 8y agowhy would you ever not want to like something? liking something is universally better than disliking something.
- 21 8y agoThis is tragedy. We'll no longer be able to say "lol, no generics" :(
- _ph_ 8y agoI am very excited to see the draft designs. They point to some good progress with some open design aspects with Go. The error handling concept looks to me as if it is ready for implementation in one of the next Go revisions. The fundamental of Go error handling vs. an exception mechanism always was, that Go errors are returned as part of function results. The calling function gets the result and the error and then can decide how to deal with it - handle the error, or return from itself. Exceptions not only mean automatic returns, but also automatic stack unwinding across all functions that do not implement an exception handler. I have always preferred principle of the Go error handling to exceptions. What is great about the error handling proposal is, that it is fully contained inside each function. It does not change the signature of functions, they still return an error amongst the return values or not. You cannot see when looking at a function, whether they use the proposal or not. But like the defer statement, they offer an elegant way to schedule effects to happen, when a function returns. A plain return will trigger all deferred function calls, a check "catching" and error will trigger all handlers and then all deferred functions. Furthermore check works as a filter, so chaining functions which return values and errors becomes much easier. I also like the choice of "check" vs. "try" or "?". A short word reads better than an operator character and try is too familiar with an exception mechanism, what check/handler is not. On the first glance, the handlers looked a little bit confusing about the logic flow. But if one considers check/handler the twins to return/defer, they become very obvious and fit very well into the existing framework. Handler can be considered a deferred function for the return in the error case, and check as triggering that error case return. On the generics, the concept is not finished yet, but what is there so far looks like to be on a good path to adding generic types to Go without making it a much more complex language or too many sacrifices like the type erasure in Java. On the syntax side, I like that they managed to find a syntax, which uses the normal parens used for function and method syntax before, as this makes the code a bit nicer to read. I am very curious how they develop.
- fdr 8y agoHeh, it's hard to get someone more qualified to look at this issue than Ian Lance Taylor. An unknown member (by most) of the Go team that more ought to know about.
- adamwk 8y agoOne nit-pick in the generics overview. It claims Swift introduced generics in Swift 4, but parametric polymorphism has been in the language since Swift 1, though refined after each subsequent release.
- slavapestov 8y agoThis is also incorrect: > Equatable appears to be a built-in in Swift, not possible to define otherwise. Equatable is a standard library type.
- dbaupp 8y agoThey may trying to refer to the automatic synthesis of Equatable conformance.
- munificent 8y agoThis is very exciting! One challenge with introducing generics after the language has shipped is that the language was already designed around their absence. In particular, there are a number of "generic" or "polymorphic" that are built into the language. In order to handle them specially, they often have special-purpose syntax. Examples (note: I'm not very familiar with Go, so I might have some of this wrong): * Addition, which is overloaded for various number types, uses "+". * Accessing array and map elements uses "[...]". * Equality, which is overloaded for various types, uses "==". Having dedicated syntax for these makes sense in Go 1 because they are special. They do things user code cannot do. Prohibiting operator overloading reinforces this. If you see a "+" in code, you know it's the magical built-in "+" operator. But with generics (and potentially overloading), these operations no longer need special powers. It's possible to write your own collection type with a type-parametric "subscript operator". You just don't get to use the "[...]" syntax. This is particularly problematic because it interacts poorly with contracts, which specify syntactically which operations are used. Most of the contract examples in the doc here use "==", "+", and "[...]". So even though you have generics and constraints (contracts), it's very hard to define a generic function that works with both built-in generic types (slice, map, etc.) and user-defined ones. The end result is probably going to be something similar to Java where the "best practice" is to use named methods for all operations ("Add()", "Equal()", etc.) and then provide wrappers that lift the built-in types into this more flexible world like you do with ArrayList instead of raw arrays in Java. That sucks because the built-in syntax is better. It's inane that Java has array literals, a nice subscript operator, and a terse ".length" property but 99% of Java code doesn't get to use any of them when working with "arrays". Instead of: array[0] = array[array.length - 1]; It's all: array.set(0, array.get(array.size() - 1)); I could certainly be wrong, but it seems Go is on that same path. Aside from this, the generics proposal looks really neat to me. The limitations on methods will also likely be very painful, but it's a hard problem with no easy solution given Go's approach to structural typing, methods, and the desire to not require runtime specialization. Language design is hard. Language evolution is 10x harder.
- ainar-g 8y agoWhat about sum types? During 4 years I've been programming Go professionally I missed sum types more times than I missed generics. Writing yet another interface SumType { isSumType() } won't kill me, but it does get annoying.
- arianvanp 8y agofor sure. It would also be lovely to add sum types to Protobufs too, while we're at it. many message protocols are very neatly described with sum types in my opinion. Also, errors would be way more elegant as a sum type: instead of: err, a := x if err != nil { } we can do: switch a { Err(err) -> {}, Ok(a) -> {}, } and avoid null pointers :) and then "check" is literally just "bind/ try!, flatMap, =<<" or however your language du jour calls it :)
- mayoff 8y agoRegarding Protobufs, did you mean something other than `oneof`? https://developers.google.com/protocol-buffers/docs/proto#oneof https://developers.google.com/protocol-buffers/docs/proto#on...
- weberc2 8y agoAnnoying and the performance is terrible, since pretty much every instance ends up being an allocation, and allocations in Go are quite a lot more expensive than in other GC languages. Further, it doesn't quite have the same semantics--if you return one of these interfaces, your caller still doesn't know what all of the permutations they have to check for.
- codr4 8y agoI have nothing more substantial than a gut feeling to back this up, but it all feels half baked and too conservative to me. Like they cranked out some special syntax and new concepts that sort of solve their problems and called it a day. It may look like the path of least resistance initially, but sooner or later the features will start stepping on each others toes.
- enneff 8y ago> it all feels half baked and too conservative to me Your gut is wrong. These proposals have been in development for years. The author of the generics proposal has been thinking about the problem for nearly a decade.
- codr4 8y agoSorry about the toes, but identifying with a programming language will always get you into trouble. My gut is fine, thank you very much. They're C heads, trying to build a simplified C; at least they were, they sort of messed that up with garbage collection, wild west threading and using reflection for all the wrong reasons. Sure, now the tone is different. But back in the days when the language was first released, they would make fun of anyone mentioning generics or exceptions.
- deleted 8y ago[deleted]
- thejerz 8y agoI've never understood why Go tries so hard to reinvent exceptions. I've read their manifesto, their books, and this new exception syntax, but I'm not convinced the problem they're describing exists; and, even if it does exist, that their implementation solves it. Exceptions in Ruby, Python, Java, C#, etc. are a tried-and-true paradigm for handling unexpected code paths, with a clear syntax: the only problem with exceptions is that developers don't use them, or don't use them properly, or aren't forced to use them (e.g, checked exceptions, a la Java's throws), all of which can be fixed with compilation rules. As someone who writes defensive code, and utilizes a lot of exception throwing/handling for 14+ years, Go's lack of a rational exception framework is a big turn off.
- jimmy1 8y ago> a tried-and-true paradigm for handling unexpected code paths, with a clear syntax Citation needed here. Who seriously has this opinion? There was just a Java/JVM conference on the very topic of how to make exception handling better. Tried? Yes. True with a clear syntax? Definitely not.
- ocfx 8y agoI think their shoutout to Rust at the bottom of the document on exceptions is really where they should be focused. Using things like Maybe/Either is a vastly superior approach to the error handling process which has been demonstrated by languages like Elm, Haskell, and Rust.
- IshKebab 8y agoThere are serious problems with exceptions. 1. It's often not clear which exceptions can be thrown from a function. I don't think many languages force you to list them all. In most languages it is a case of "this function can throw any exception it wants - read the source code (and of all the functions it calls) if you want to know which". 2. Adding context to exceptions is even more verbose than Go's error handling. In the video for this post for example, where they have fmt.Errorf() for each line that might fail you can easily add context like "opening file failed: ..." or "parsing file failed: ...". The equivalent with exceptions is a separate try/catch for every line! I've never seen anyone bother with that. This seems like a bit of a flaw with the "handler" approach by the way. Rust's map_err() is a bit nicer.
- deleted 8y ago[deleted]
- eximius 8y agoYou could almost write a `check!` macro for rust that would have the same semantics. You can obviously already write one when no `handle` exists (it'd just be `try!` or `?`), but I can't think of how you'd build up the handles like they describe without a procedural macro. You likely could do something semantically equivalent with procedural macros. `handle!{ block }` would define a block to inline later when needed and then `check!` would do the normal thing but also inline that block, just like Go's. Also, I feel like it is a little disingenuous to say that Rust has `no equivalent of handle` when it has something so similar in `Result::or_else(self, Op)`. You don't get the handle accumulation and you have to pass in a closure at the site, but they're remarkably similar.
- sustain168 8y agowhen can Go Lang get rid of the address reference(*) shit? checkout java and python.
- skybrian 8y agoThis is just syntax, but I'm curious about the decision to go with regular parens for supplying type parameters, rather than the familiar angle brackets, or perhaps Julia's choice of curly brackets?
- gameswithgo 8y agoangle brackets may be harder to parse, remember that compile times are an important go feature.
- yiyus 8y agoRead the document: https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md#why-not-use-like-c_and-java https://go.googlesource.com/proposal/+/master/design/go2draf...
- IshKebab 8y ago> A more robust version with more helpful errors would be.... My issue with that example is that it isn't like that. People don't use fmt.Errorf() with exactly the same string on every line. They like to add per-line context. Then you're back to one handle block for every line and it's even worse.
- sukunrt 8y agoA question on generics from the docs if the declaration is func Keys(type K, V)(m map[K]V) []K why do we need to call it like keys := Keys(int, string)(map[int]string{1:"one", 2: "two"}) can't the types int and string be inferred from Keys(map[int]string{1:"one", 2:"two"})
- incompatible 8y agoIt seems to be lacking a mechanism for accumulating multiple errors, which can be done using a package like hashicorp/go-multierror. The usage pattern is that you have a complex function that does something like parse a document and return the data from it. But the file format is so complex, that a lot of minor problems can occur, but they don't necessarily prevent you getting some data out. So the return value is the data and a set of errors (or warnings, if you like).
- ainar-g 8y agoThis might actually be addressed, albeit very quickly, in one of drafts: https://go.googlesource.com/proposal/+/master/design/go2draft-error-printing.md#error-trees https://go.googlesource.com/proposal/+/master/design/go2draf.... >To implement formatting a tree of errors, an error list type could print itself as a new error chain, returning this single error with the entire chain as detail. Error list types occur fairly frequently, so it may be beneficial to standardize on an error list type to ensure consistency.
- deleted 8y ago[deleted]
- tomc1985 8y agoYay, more tech churn...
- norswap 8y agoThe case against exceptions is unfair to Java, whose model is CHECKED exceptions. Of course, people hate them, but they are semantically equivalent to the new proposed solution, which is itself much better than current error handling in Go, which is frankly horrid. I must admit the check/handle mechanism is syntactically more pleasant though, avoiding surrounding code with try/catch.
- evincarofautumn 8y agoAs a type theorist and programming language developer, I’ll admit that’s a fairly reasonable design for generics. I’m still a bit disappointed by the restrictions: “contracts” (structural typeclasses?) are specified in a strange “declaration follows use” style when they could be declared much like interfaces; there’s no “higher-level abstraction”—specifically, higher-kinded polymorphism (for more code reuse) and higher-rank quantification (for more information hiding and safer APIs); and methods can’t take type parameters, which I’d need to think about, but I’m fairly certain implies that you can’t even safely encode higher-rank and existential quantification, which you can in various other OOP languages like C#. Some of these restrictions can be lifted in the future, but my intuition is that some features are going to be harder to add later than considering them up front. I am happy that they’re not including variance now, but I feel like it’ll be much-requested and then complicate the whole system for little benefit when it’s finally added.
- kyrra 8y agoThey are truly looking for feedback on these proposals. If you have thoughts on how could change to be potentially more compatible for future additions, consider doing a blog post write up and submitting it to them.
- ianlancetaylor 8y agoThanks for the comments. We tried for some time to declare contracts like interfaces, but there are a lot of operations in Go that can not be expressed in interfaces, such as operators, use in the range statement, etc. It didn't seem that we could omit those, so we tried to design interface-like approaches for how to describe them. The complexity steadily increased, so we bailed out to what we have now. We don't know whether this is is OK. We'll need more experience working with it. I'm not sure that Go is a language in which higher-level abstraction is appropriate. After all, one of the goals of the language is simplicity, even if it means in some cases having to write more code. There are people right here in this discussion arguing that contracts adds too much complexity; higher order polymorphism would face even more push back.
- 8y ago
- marcrosoft 8y agoTo the Go authors: * I've never seen a language rise in popularity as fast as Go. * Go is doing something right. * Please refrain from changing the language.
- otabdeveloper2 8y agoWe need a variant of Greenspun's tenth rule for systems programming languages. Any sufficiently old systems programming language that aims for simplicity and clean code will eventually devolve into a variant of C++, except ad-hoc and poorly specified.
- scns 8y agoWhat the Go team is doing seems diametrically opposed to "ad-hoc" to me.
- infogulch 8y agoThese are all great starts, I'm glad the Go team is finally able to get some purchase around these slippery topics! Unless implied constraints are severely limited, I don't think they're worth it. The constraints are part of the public interface of your generic type, I'm worried that we could end up with an accidental constraint or accidentally missing a constraint and having the downstream consumer rely on something that wasn't an intended behavior. For Go 2, I would really like to see something done with `context`. Every other 'weird' aspect of the language I've gone through the typical stages-of-grief that many users go through ("that's weird/ugly" to "eh it's not that bad" to "actually I like this/it's worth it for these other effects"). But not context. From the first blog post it felt kinda gross and the feeling has only grown, which hasn't been helped by seeing it spread like an infection through libraries, polluting & doubling their api surface. (Please excuse the hyperbole.) I get it as a necessary evil in Go 1, but I really hope that it's effectively gone in Go 2.
- majewsky 8y agoHow would a replacement for `context ` look? Also, I don't share your sentiment. I actually find Context to be a quite nice, minimal interface. Having a ctx argument in a function makes it very clear that this function is interruptible. I certainly prefer this over introducing a bunch of new keywords to express the same.
- atombender 8y agoThe problem with context isn't necessarily the interface, it is that it is "viral". If you need context somewhere along a call chain, it infects more than just the place you need it — you almost always have to add it upwards (so the needed site gets the right context) and downwards (if you want to support cancellation/timeout, which is usually the point of introducing a context). Cancellation/timeout is important, so of course there have been discussions of adding context to io.Reader and io.Writer. But there's no elegant way to retrofit them without creating new interfaces that support a context argument. Cancellation/timeout is arguably so core to the language that it should be an implicit part of the runtime, just like goroutines are. It would be trivial for the runtime to associate a context with a goroutine, and have functions for getting the "current" context at any given time. Erlang got this right, by allowing processes to be outright killed, but it might be too late to redesign Go to allow that. I'm ignoring the key/value system that comes with the Context interface, because I think it's less core. It certainly seems less used than the other mechanisms. For example, Kubernetes, one of the largest Go codebases, doesn't use it.
- leibwiht 8y agoDid anyone else ctrl-f for "generics" first thing upon opening this thread?
- dakom 8y agoWhat sorts of things can you do in the Haskell type system that you would not be able to do with the Go generics proposal?
- edflsafoiewq 8y agoHigher-kinded types? Monads, etc.
- dakom 8y agoCouldn't one define Monads, etc. with interfaces, once they allow generics? (there's nothing in Haskell which enforces the category laws in the typesystem, afaik)
- ivanjaros 8y agoAfter a year with Go I have to say that I am quite comfortable with it. The beginning was an absolute hell but once you open up your mind a bit and accept that it is what it it and if you want to use it YOU have to accept its concepts. And time heals all wounds :D From those three points - the error handling seems a waste of time to even bother with implementing. The way it works now is fine. The error values - yes, there should be something done. As mentioned in the draft, you often need information on where the error occurred(func, line, file) since the error can travel in between libraries, you have dynamic messages so you cannot declare global variable to compare the error with based on string content and lastly, error code would be nice too(severity/http status code). Generics - I have to say this is pain for most people, I got used to work around it via wrappers and so far I'm good. I mean, if you have argument as interface{} and you accept bool or int then why don't you just have Value{b bool, i int} struct and just check which one it is and proceed with the logic? I have typed data with 23 different types and a proxy that implements all interfaces with GetType, SetType and AsType methods and yes, it is long code to write for something so trivial but in the end it works and I can now go on with my life. Internally, gRPC does the same when it generates Go client. To me, one of the things I miss the most is ternary operator, that would save a ton of code. And I would also welcome removal of nil maps. var foo []string can be used with append but var foo map[string]string cannot be used as foo["one"]"two" because you have to foo := map[string]string{} first which means you have do add 4 lines of unnecessary, imho, code everywhere you work with maps to avoid panic due to map being nil and not just empty.
- ivanjaros 8y agoAlso they could implement ``` to allow ` in strings
- pietherr 8y agoGo 2 considered harmful
- the_grue 8y ago(I usually avoid ranting, but just couldn't resist it this time around. Go on, bring on your downvotes, you still know deep inside this is the truth.) Oblig. generic laughter [1] Are Go's problems over, then? I have read the error handling draft so far, and girl, was I in for a big surprise. Imagine a few nested try-catch blocks, where all the try{} and catch{} are written... imperatively, one after the other, with both try{} bodies and catch{} blocks interspersed in the same scope. E.g., line 1: "catch" block, lines 2-10: "call IO" statements, line 11: another "catch" block, etc. The errors are always passed to the previous "catch" block, then the one before that, etc, until the error has been handled. Haven't we seen something like this before? Ah yes, the goto statements! This was designed by a person with a severe case of scope-o-phobia. If that person has designed the rest of Go (and I think she has), the language might be unsalvageable. Just start from scratch, and do it right this time around. Oh wait, no need - we already have Kotlin. /rant [1] https://www.youtube.com/watch?v=iYVO5bUFww0 https://www.youtube.com/watch?v=iYVO5bUFww0
- JulianMorrison 8y agoI don't recommend any attempt to unify check/handle and panic/defer/recover. They don't behave the same because they aren't doing the same: one is controlled handling of normal errors with reduced boilerplate, the other is "crashing with cleanup".
- floatingsmoke 8y agoA bunch of pioneer project like docker, kubernetes, etcd, prometheus etc. has been built with go and I don't believe that the maintainers suffered lack of generics and error handlers. On the other hand, as a new go programmer, I can really dive into their code base and understand each line of code without thinking twice. This comes from simplicity. But these possible nested error handlers and generics will lead developers to think more than twice during writing or reading a code base. These ideas is not belong to go era but Java, C++ etc which go doesn't wanted to be like. Someone here has mentioned that internal support of generics for slices, maps and other primitives. I think this can be the best solution for generics in go. For the error handling I think more elegant way could be found. Please do not rush.
- JulianMorrison 8y agoIt sounds like nested error handling will be the abnormal case and the "default" error handling will often be normal. That is, all you will notice in common use is that the boilerplate "if" is replaced with a "check". It also sounds like the nesting does not escape the function itself. It always either returns error, or doesn't.
- floatingsmoke 8y agoWell, How about multiple return values? How will "check" keyword handle return values other than error? func ReadFile(path string) (string, error) { b, err := ioutil.ReadFile(path) s := string(b[:]) return s, err } How this function would be written regarding to drafts?. I am really confused.
- deleted 8y ago[deleted]
- secure 8y agocheck returns all non-error return values, so the function would be written as: func ReadFile(path string) (string, error) { b := check ioutil.ReadFile(path) return string(b), nil }