The only thing that isn't so forgivable with Go is that these aren't new problems this time around. Go is still struggling to get over challenges that were first seen a long time ago. It's a great experiment on designing for complexity vs trying to avoiding complexity.
It's not surprising, it stems from the arrogance of Go designers who think they can eliminate complexity by deeming it irrelevant and making the user carry the weight of the complexity they refuse to deal with. Simplicity isn't hard, like Rob Pike says, it's a trade off.
If you're not going to have enums in your language for instance, you are forcing your users to implement their own, badly and in incompatible ways.
If you're not going to have generics, well you'll get this stupid situation where users are expected to "augment" the compiler with code generators, leading to an increase in complexity in building a program, or worse, ignoring compile time type checking since it's the path of the least resistance when dealing with generic container types.
And I'm all for the idea that relational fields should be NOT NULL. I also fear that this doesn't really work for backwards compatible thinking. If I serialized some data down to disk before a field existed, I don't expect it to be there when I check it later.
You can be tempted to think it should just be the zero value of the type you are using. Or you can add some extra boilerplate around accessing. I think either works. Just make sure you aren't getting carried away. And, try to do anything that cares about the absence or presence of something at a layer from where you get that something. Don't punt the decision down your codebase.
(That is, Optionals are great at the layer, don't pass them as parameters to inner code, though. Obviously, YMMV. And, quite frankly, probably will go further than mine.)
What if the data is actually missing? How else do you record that information?
Conceptually NULL or nil is an appropriate concept for results that have no meaning, such as if an error occurred or if a passed value is not required or valid. (Though some structures can contain data that is 'incomplete' or 'not checked' and thus while a valid structure might not be 'validated' in the sense of conforming to a more specific set of expectations.)
I avoid the word "empty" when referring to anything SQL related, as it is ambiguous in three value logic.
What happened to "lightweight typesystem that reduces cognitive load"?
The intention was great and the result wasn't that great, but it still works pretty dann well. Go is an open language and they are asking for well thought out proposals on where & why the problems exist. Followed by ideas and/or examples to make it better so let's all try.
I think Maybe Types would be an amazing feature to add. Closed types would also be an outstanding win from a UX perspective. Neither of those concepts would add more cognitive load then they remove in my opinion.
From what I've seen, this holds only as long as you keep the proposals minimal and restricted to aforesaid hacking around the limitations built into the language. I'm happy to be shown evidence to the contrary: have there ever been any proposals, reacted to in a not-completely-negative way, that were like "uh, maybe we didn't have the right idea about <something basic>, let's do this instead"?
I'll argue there won't be. Every community has a culture: Go's is delightfully warm, friendly, and inclusive, but also surprisingly distrustful of learning that there are easy-to-understand but powerful language features they could be using to write maintainable code without "getting a PhD in type theory from the nearest university" (to strawman a certain [type of] person [I've often encountered when arguing about these things]).
Go has done many things right (aside from the community, good concurrency and really fast compiles come to mind) but language design is not one of them.
Is it, really? I haven't seen a more hostile open source project to outside ideas / requests regarding to language itself.
It's just open source.
Oberon is an example of a true lean programming language. The complete language reference takes up only sixteen A4 pages. The compiler OBNC implements the latest version of the language:
func DoStuf(i ILoveGoer) {
i.LoveGo() // Panic on nil
}
its hard to reason about because it doesnt look like you have a pointer, looks like you definitely have a value. IMO a nil should not be allowed for an interface. So the only way to create an interface var is in conjunction with assignment. type JoeLovesGo struct{}
func (jlg *JoeLovesGo) LoveGo() {
fmt.Printf("Joe Loves Go! jlg is %v\n", jlg)
}
( Playground: https://play.golang.org/p/kanq_mSmaI )Now, if this (admittedly uncommon) use-case is worth it's downsides is a different question, but that's how it's set up now.
The only other option is to not have nil values.
Rust has a bottom type (!)[0] without it implementing all traits by default while using a different type (Result) for error propagation. Plus having nil/null as a a value of the bottom type violates some aspects of bottomness.
[0] https://github.com/rust-lang/rfcs/blob/master/text/1216-bang...
Once you have an either type, you can also get rid of nil entirely since a Maybe type is trivially created with an Either.
Designing languages without a null value (other than for c-interop via e.g. `C.null`) is a solved problem.
https://crystal-lang.org/api/Nil.html https://crystal-lang.org/docs/syntax_and_semantics/union_typ...
var b *bytes.Buffer
var r io.Reader = b
fmt.Println(r == nil)
We might need to use other expressions to capture the _nil_ type of above assignment but we should enable the _value only_ equality check with `r == nil`I have thought a bit about it but I couldn't come up with good situations.
while nil is assigned to t2, when t2 is passed to factory it is “boxed” into an variable of type P; an interface. Thus, thing.P does not equal nil because while the value of P was nil, its concrete type was *T.
Honestly if you aren't a skilled enough programmer to navigate the nuances of any particular language then you really are no better than kids playing in drag-and-drop environments like Scratch.
result, err := Foo()
if err != nil {
...
}
over and over again in a language designed after 2000. The usual argument is that Go's simplistic design "reduces complexity", yet explaining something basic as why the error handling system doesn't behave the way one expects needs you to know how the compiler represents interface types.The next decade will see Go adding in most or all the complexity real-world software asks for, without ever admitting that maybe it should've been supported from the beginning without the hacky workarounds.
> Honestly if you aren't a skilled enough programmer to navigate the nuances of any particular language then you really are no better than kids[...]
Even though the bit in italics is pretty much the opposite of the "Go/JS pitch", I'll bite.
Sure, learning to code at a high level means you need to take time to learn things (which is the opposite of the pitch). I just don't get why teaching yourself to "navigate the [brokenness]" of a language that was out-of-date the day it was released is preferable to learning to write in an expressive language that won't artificially handicap you or provide you with an arsenal of footguns.
In all the time you were casting to and from interface{}, you could be exploring and using powerful and practical new ideas that make it less likely you'll suffer for failing to check one of those "err"s.
Public cosplay of [presumed] intelligence as a new smoking.