14 ms·
Go and Simplicity Debt Redux
- xyzzy_plugh 9y agoPrevious article in series: https://dave.cheney.net/2017/06/15/simplicity-debt https://dave.cheney.net/2017/06/15/simplicity-debt And corresponding discussion: https://news.ycombinator.com/item?id=14560471 https://news.ycombinator.com/item?id=14560471
- m_sahaf 9y agoActually, the "Go and Simplicity Debt Redux" is not the same as the one you referenced. The current post is part 2.
- xyzzy_plugh 9y agoThat was what I was going for :) I'll clarify.
- taeric 9y agoI certainly appreciate the desire to avoid two main ways of doing things. Curious about the race conditions being common. Is this from go encouraging go routines? Seems most programs don't need or use multiple threads for a single task.
- coldtea 9y ago>Right now, to understand how io.Reader works you need to know how slices work, how interfaces work, and know how nil works. If the if err != nil { return err } idiom was replaced by an option type or maybe monad, then everyone who wanted to do basic things like read input or write output would have to understand how option types or maybe monads work in addition to discussion of what templated types are, and how they are implemented in Go. God forbid we'd all have to learn such a simple concept (in a language with several non-orthogonal concepts and special cases and all the channels coordination nuance that lack of generics prevents of abstracting in a standard library to boot). >Obviously it’s not impossible to learn, but it is more complex than what we have today. Well, adding a feature we'll always be more complex than not adding it. In the end, simple assembly style instructions are the simplest both for implementation and for learning fewer concepts as a developer. But abstractions make things easier to write and read after one has learned their concepts. And it's 2017, even "lowly" JS developers understand such functional concepts today... >What began as the simple request to create the ability to write a templated maybe or option type has ballooned into a set of question that would affect every single Go package ever written. This is more similar to how some molehills grow into mountains. People have been asking for generics as a standalone feature first and foremost -- orthogonal from replacing error handling. Now suddenly we can't discuss adding Generics without taking into consideration the big burden of changing the error handling too? >On the surface this sounds like a grand idea, especially as these types are leaking into the standard library anyway. But that leaves the question of what to do with the built in slice and map types. Should slices and maps co-exist with user defined collections, or should they be removed in favour of defining everything as a generic type? Whether the answer, the mere addition of the ability to have standard user defined generic collections and collection libraries sounds awesome in itself, regardless of what would happen to the ill-thought maps and slices that specialcased genericity in the backend. This doesn't seem like a discussion of how to best add generics, but more like analysis paralysis. >David Symonds argued years ago that there would be no benefit in adding generics to Go if they were not used heavily in the stdlib. The question, and concern, I have is; would the result be more complex than what we have today with our quaint built in slice, map, and error types? A better question is: would some more complexity hurt us, especially beginning with in an overly simplistic language, and even more so, when said complexity actually unifies and harmonizes ugly special cases.
- andmarios 9y ago>People have been asking for generics as a standalone feature first and foremost -- orthogonal from replacing error handling. There are many languages out there that have generics. Go is build on different principals. Just use something else, no need to enforce your ideas on people who don't want them. Maybe go users should start going into every language discussion they can find and ask for generics to be removed.
- pcwalton 9y agoIf you can successfully argue that generics should be removed from other languages, then you should feel free to argue that. The reason why nobody is arguing that is that it's hard to argue successfully, because simple ML-style generics are incredibly useful and have negligible if any drawbacks in virtually every statically typed language.
- andmarios 9y agoI would participate in an argument only for a language that I am invested in. When it comes to go, most people who argue about generics, do not write go and probably wouldn't touch it even if it had generics. Also, an important issue with such arguments, is that generics is a feature, so obviously it is an easy side to choose for an argument. You can either have or don't have a feature. People will instinctively select the have option because common sense dictates that it is better to have something even if you don't use it. People want to quantify an argument and having is better than not having. People don't want to argue about language design —there are few people on the planet that could really argue about language design anyway-, they need language MHz to compare.
- coldtea 9y ago>I would participate in an argument only for a language that I am invested in. When it comes to go, most people who argue about generics, do not write go and probably wouldn't touch it even if it had generics. Citation needed. >Also, an important issue with such arguments, is that generics is a feature, so obviously it is an easy side to choose for an argument. You can either have or don't have a feature. People will instinctively select the have option because common sense dictates that it is better to have something even if you don't use it. People want to quantify an argument and having is better than not having. This non-argument could be applied against adding ANY feature at all. The idea that there are more enlightened users who care about "language design" and the unwashed masses who want to add everything and the kitchen sink to Golang and spoil it does not hold water (Besides, even if it was true, people who care about language design do not particularly see 90% of Algol 68 + CSP as anything to write home about). People dont ask for all kinds of BS to be added to Go -- the ask for certain things that are actual pain points. And the list the 2016 Survey post gives is quite sensible (Generics, better error handling, better packaging, etc) and specifically applicable to Golang pain points.
- kmicklas 9y agoBasically all these articles about Go make it sound like it was designed to be some kind of teaching language with a low conceptual barrier to entry, except Google intends it to be used in real settings, which is the problem.
- computerex 9y agoWhat articles are you talking about? This couldn't be further from the truth. Go is a practical language meant to be used in production settings. It's not a haskell. For example, it's support for protocol buffers is second to none imo.
- chowells 9y agoErr. Why do you think Haskell isn't intended to be used in production settings?
- skybrian 9y agoThe slogan "avoid success at all costs" suggests that they don't want a lot of production usage, at least.
- lclarkmichalek 9y agoIt's "avoid 'success at all costs'", which has _quite_ the different meaning. Although, if the slogan has too high of a barrier to entry, I don't know what you'd think of the language itself.
- mrkgnao 9y agoNo, it's meant as a warning against the trappings of explosive popularity. A language like C++/Java/JS is difficult to modify because of how entrenched the usage of the language becomes. In that sense avoiding "success at all costs" (which is how it should be parsed) allows "agile" development of the language.
- nvarsj 9y ago
- comex 9y agoRather than extending number-of-return-values overloading to user-defined methods, I’d say just deprecate it altogether. It might have made sense in a world where generic functions are magic and weird and so you want to minimize the number of them (but on the other hand don’t necessarily mind making them even more magic). But in a Go with generics, operations like “access value that may not be present” can just be regular named methods. That applies to both user-defined collections and the builtin ones. Would help pay down the debt...
- YZF 9y agoAren't these just two different operator[] ? value, exists := usertype["something"] value := usertype["something"] func (t *usertype) operator[](k KeyType) (value) func (t *usertype) operator[](k KeyType) (value, bool) Should work, no? (EDIT: usertype would be the templated type, so <userType> or whatever the syntax ends up for that. so the above would be the instantiation of the template for a particular type)
- TheDong 9y agoIf you have sum types, multiple return is less useful. if you have proper tuples and destructuring assignment, you can entirely remove that abomination. Right now, it's impossible for a user to define a method that can return 1 or 2 values depending on how it's called. Only stdlib types, like map and channel, get to do that. It's fuggin horrific, and spreading that further to user defined functions would be a mistake.
- YZF 9y agoMy point is that you don't really know if the stdlib types actually have one method that returns 1 or 2 value or two methods one returning 1 value and the other returning 2 where the compiler chooses based on the call site which one to call. Just because [] looks like one method to the user doesn't mean it has to be one method. Having two different functions is a way of dealing with this while minimizing change as it's just syntactic sugar and not a language change. Variable length tuple types are a much bigger change. They'd also presumably be a lot more expensive. Ideally a map type implemented by the user should be as performant as the built-in map type.
- YZF 9y agoA few random thoughts: - C++'s std::array is an example of marrying fixed sized arrays with templates. - I'd say keep the built-in types and their behavior unchanged but offer a way of providing those for user types. I guess the Deletable interface is one idea but maybe the "delete" override should have nothing to do with interfaces. I think something like func (t Type) operator delete(k KeyType) ? I guess in Go a function is an interface but I wouldn't start with the interface here. - Allowing e.g. delete or slicing on user types does not need to imply those types are polymorphic with the built-in types. This might be a little confusing but I think it's still worth while to draw that line. I don't think it's necessary to allow creating a function that takes a Deletable to take either the built-in map type or a user type. - range support requires some sort of iterators. Could be done through co-routines like Python's generators or something more like C++ iterators? co-routines could be a cool addition to the language :) I think you'd want to try and "contain" the change as much as possible. More syntactic sugar than fundamental changes and as little change as possible to the built-in types. Otherwise you end up with a completely new language.
- kjksf 9y agoAnd how many decades did it take for C++ to support std::array? What people seem to ignore is that templates are a bottomless pit of complexity. Swift is on version 3 and they still didn't finalize the semantics of generics. Not due to lack of trying. Swift was trying to design and implement generics from day 1 and they still haven't. That's how complicated designing generics is. I prefer stable language with fast compiler to Swift/Rust situation, with language taking years to mature and very slow compiler. Judging by popularity of Go, so do many other people. While the languages probably will mature after couple more years, I don't see much hope for making the compiler as fast as Go, regardless of the effort. See C++ compilers.
- pjmlp 9y ago> And how many decades did it take for C++ to support std::array? About one decade. In the mid-90's already most C++ compilers had their own version of std::array, for example Borland compilers.
- 9y ago
- ori_b 9y agoThis all sounds rather awful to me, honestly. If this is the path that Go takes, I'd rather not have generics added. Which is a shame. I like generics, and I implemented them in Myrddin (https://myrlang.org https://myrlang.org) for a reason.
- grasleya 9y agoMore golang Stockholm syndrome. These arguments are always weird to me. Go has plenty of complex features that go developers don't seem to think cause too much cognitive burden (automatic gc, structural subtyping, etc.). Why is it that relatively simple and ubiquitous language features like exceptions and generics are just way too much and cause an unacceptable amount of complexity? I just don't buy it. Turn the argument around. Are there any Java, C++, etc. developers arguing for the removal of these features from their languages? Are there people who say that even though these languages have generics you shouldn't touch them because they're bad? Do any devs with option/maybe types really want to go back to unsafe code that can fail if you forget to check for nil/null? Show me someone who hasn't drunk the go kool-aid who still makes these sorts of arguments and I might actually start listening.
- dangerlibrary 9y agoPersonally, I think checked exceptions are sometimes a headache in Java and I've worked in places where the default when encountering a checked exception was to catch and throw RuntimeException with a helpful error, which is pretty similar to Go's error/panic model.
- grasleya 9y agoChecked vs unchecked exceptions is kind of a separate thing from exceptions vs no exceptions. There are plenty of modern languages without checked exceptions but few with no exceptions period.
- kasey_junk 9y agoGo has exceptions in the form of panics, they are discouraged and less sophisticated than other language exceptions but they certainly exist.
- int_19h 9y agoThere is a valid argument about whether exceptions are the best way to signal errors or not. But most modern alternatives are some form of capturing errors directly in the type system, so that return types can carry error information (option or error types etc), and so that callers are forced to check for errors.
- alkonaut 9y ago> Everyone who wanted to do basic things like read input or write output would have to understand how option type, in addition to discussion of what templated types are I don't necessarily think that's a bad thing. Something that may be null is already an option type. It's "some T or null". Making the compiler force you into checking which it is, is no difference from checking for the presence of null. There is literally no difference in complexity - it's actually simpler because you don't have the cognitive overhead of remembering where a certain variable has and hasn't been null checked (After a null check, your variable has magically changed type to "some T, definitely not null".) x = getsomething if x != nil dosomething(x) and x = getsomething if x.hasvalue dosomething(x.value) The programmer alone has to remember this - remember to check, and the status of each variable that can be null. Was x checked before? scroll up to see... Add 10 variables and some nested ifs and ask the programmer which variables can and can't be null at any given place. This is the job of a compiler, not a programmer. And if my experience is any guide, the less experienced programmers, which are the ones we are discussing now, would be happy to have 100 locals or 12 levels of nested ifs in a method. They need this added help more than the people who ask for it.
- catnaroek 9y agoNo, nulls aren't options. Options nest. You can have Some(Some(Some(foo))). The non-compositionality of null makes it impossible to abstract it away.
- brandonbloom 9y agoHuh? What kind of "nesting" are you talking about? How is that different than nesting structs containing nullable pointers?
- catnaroek 9y agoIn ML, if you have a type “foo”, then you can also use the type “foo option option option” without creating any new nominal datatypes. In particular, using a value of type “foo option option option” is just as easy as using a value of type “foo” or “foo option”: case wrapped_foo of NONE => ... | SOME NONE => ... | SOME (SOME NONE) => ... | SOME (SOME (SOME foo)) => ... On the other hand, with your proposed alternative, you have to create a wrapper struct for every nullable kind of thingy: type MaybeFoo struct { foo *Foo } type MaybeMaybeFoo struct { foo *MaybeFoo } type MaybeMaybeMaybeFoo struct { foo *MaybeMaybeFoo } And, as if that weren't offensive enough, you have to manually pack those pointers into structs and then unpack them back.
- DelightOne 9y agoGo doesn‘t want to decide on generics, Swift doesn‘t want to decide on multi-threading. Put them together in a room, maybe they can help each other.
- hota_mazi 9y ago> A powerful motivation for adding generic types to Go is to enable programmers to adopt a monadic error handling pattern. That has never been my impression. The main drive for generics is safer code, period. It doesn't necessarily translate to a different way of handling errors (Java and Kotlin mostly use exceptions despite supporting parametric polymorphism). Rejecting generics because they would force a switch to monadic error handling is completely misunderstanding the value of generics, a sentiment that seems to be widespread in the Go community.
- vanderZwan 9y ago> Right now, to understand how io.Reader works you need to know how slices work, how interfaces work, and know how nil works. If the if err != nil { return err } idiom was replaced by an option type or maybe monad, then everyone who wanted to do basic things like read input or write output would have to understand how option types or maybe monads work in addition to discussion of what templated types are, and how they are implemented in Go. I can't really follow the argument here: how would using option types replace the existing idiom? Instead of err being a pointer, err is an option type and the only thing that changes is that instead of writing if err != nill, you write if err. And if comparing nill vs option types works in isolation, then I also don't see where the jump to "in addition to what templated types are" comes from, or the connection to slices and maps. In fact, if Go had option types, would it still need nill at all? By which I mean, on the user-side of things (under the hood I suppose null will be used for implementing an option). Because if it doesn't, and I strongly suspect one doesn't in a language without pointer arithmetic (which Go happens to be), then we can simply replace every nil with an option. The discussion on how templates interact with existing features and complicate things is fairly clear, but the option types are treated as a lot more complex than they really are. In practice it is a compiler-enforced if-not-null check on pointers, because nill is a type instead of a value. That's about it. That level of compile-time enforcing also seems to be perfectly in line with the safety the Go authors want their compiler to enforce, given how strict it is with errors. You can easily use them without really understanding what a monad is - I'm living proof of that. The only extra thing you somewhat need to understand is sum types, which are a lot simpler. Then again, Go doesn't have them and their usage overlaps with interfaces - how to deal with that might be a more useful discussion regarding option types in Go.