6 ms·
Fun fact: because json is a reflective package in Go, solving your problem is quite trivial: https://play.golang.org/p/-TZ5b9Su1or https://play.golang.org/p/-TZ
by vlads_ 6y ago
Fun fact: because json is a reflective package in Go, solving your problem is quite trivial: https://play.golang.org/p/-TZ5b9Su1or 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 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.
- nemothekid 6y ago>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). I like your solution to my imaginary problem, but I am going to counter this one. Now I'm salty because nil interface[1] actually bit me once and cost me. Go's zero value approach also exists in Rust, but is type safe. Basically every primitive type, and Option<T> have zero values. Any struct that is made up of those values can also safely have zero values. Then for those that don't, you can implement your own zero values. This is called Default in Rust. Go's zero value approach is perfectly possible, and arguable better without nil. And even then, the number of times I've seen a service crash because someone "forgot" to initialize a nullable type, and passed it to a function that then exploded isn't an issue I should deal with in 2020. [1] https://medium.com/@glucn/golang-an-interface-holding-a-nil-value-is-not-nil-bb151f472cc7 https://medium.com/@glucn/golang-an-interface-holding-a-nil-...
- steveklabnik 6y agoImportant distinction: Default is something you have to opt into for your types. It is not pervasive.
- vlads_ 6y agoFirst of all, interfaces are not nil if they point to a nil value for the same reason a * * T is not nil if the * T is nil. That is the correct decision. You can not call a function on a nil interface, but you can call a function on an interface which is not nil, but where the value of the type is nil[1]. As for your proposal, there are some issues with it: not all structures have a sane default for all not optional, nilable parameters. What is the default underlying reader for a bufio.Reader? A reader which returns zero bytes? Certainly that would be more confusing to debug than a simple panic, which is what we have now [2]. There's also the fact that a zero value is just a value with all the bytes zeroed and allocating an object via language means (new) never allocates more than the size of the object and doesn't do computation. But I guess the main point would be that I simply do not have a problem programming with nil-able types. Failing to initialize in Go means writing var a *T as opposed to a := &T{} or a := NewT(), which seems like an odd mistake to make - or forgetting to initialize a member of a struct in the NewT() function. Fundamentally, I do not want to spend hours of my life dealing with safeguards which are protecting me from a few minutes of debugging. But hey, that's just me. Go isn't Rust and Rust isn't Go and that's a good thing. [1]: https://play.golang.org/p/L7iy9YBC55c https://play.golang.org/p/L7iy9YBC55c [2]: https://play.golang.org/p/Vgv73KhegKI https://play.golang.org/p/Vgv73KhegKI
- DougBTX 6y ago> not all structures have a sane default for all not optional, nilable parameters. What is the default underlying reader for a bufio.Reader? I think you're actually agreeing with nemothekid here, what you're saying is that there is no sensible default value for a bufio.Reader. In Rust terminology, that would mean bufio.Reader would not implement the Default trait. Types need to explicitly implement the Default trait, so only types where that makes sense implement it. > I simply do not have a problem programming with nil-able types Yes, that's going to make a big difference to how you feel about features that reduce the chance of errors like that. I'd hazard a guess that in C# the most common exception that is thrown is the NullReferenceException, after a quick search[1] it looks like NullPointerException is a good bet for the most common exception in Java. Most of those IlligalArgumentExceptions are probably from null checks too. > Fundamentally, I do not want to spend hours of my life dealing with safeguards which are protecting me from a few minutes of debugging. Similarly, I've already spent hours of my live dealing with null and undefined value errors, and I'd like to stop doing that. So I welcome new languages and new language features that help to stop those errors before they happen. [1] https://blog.overops.com/the-top-10-exceptions-types-in-production-java-applications-based-on-1b-events/ https://blog.overops.com/the-top-10-exceptions-types-in-prod...
- kelnos 6y agoThe first comment below your link to the message about Rob Pike's feeling on types was a more succinct form of my reaction: > Sounds like Rob Pike doesn't understand type theory. I'd be a bit more charitable; Pike is a smart dude, I'm sure he does understand type theory, but has decided he'd prefer to write imperative code. And he's actually good at writing imperative code correctly, so for him, that works. But most people are not very good at writing imperative code correctly, at least not the first time, and not without writing a large volume of tests (usually several times as much test code as program code) to verify that imperative code. Or perhaps Pike was just assuming the person he was talking to was only talking about types in the OOP sense. If that's the case, I agree with him: taxonomies are boring, and inheritance often leads to leaky abstractions. But types let you do so much more than that. I much prefer being able to encode behavior and constraints into the types I define over writing a bunch of tests to verify that the constraints I've expressed in code are correct. Why do the work that a compiler can do for you, and do it much better and more reliably than you?
- swertyb 6y ago> But most people are not very good at writing imperative > code correctly, at least not the first time, and not > without writing a large volume of tests (usually several > times as much test code as program code) to verify that > imperative code. How are languages other than Go (like Rust, Swift etc.,) not imperative? They are nearly as much imperative as Go but have FP features like ADTs and pattern matching, but those don't make them "functional". This point might be valid if you are talking about Haskell, MLs and the likes. > I much prefer being able to encode behavior and constraints > into the types I define over writing a bunch of tests to verify that the constraints > I've expressed in code are correct. Why do the work that a compiler can > do for you, and do it much better and more reliably than you? IMO, this "types replacing tests" might be true when comparing dynamic vs static typing, but for already static typed languages, extra typing cannot be a substantial gain. Personally, I find type-level programming beyond a certain extent to be counter productive and it doesn't seem to have much impact on correctness (unless we bring in dependent types).
- kelnos 6y ago
- rswail 6y agoGo is designed as a language for the average. Average programmer doing averagely complex things in the current average environment (ie web services, slinging protobufs or equivalent). That is its specific design goal for Google. It's designed to be simple, to be boilerplate, to be easily reviewable/checkable by coding teams. Not sure why Go and Rust are always the compared languages. Go is designed to replace Java/RoR/Python in Enterprise-land, not to replace C/C++. Rust is designed to replace C/C++ in system-land, embedded, kernel, thick app components (browsers are probably the most complex apps running these days on end user systems). The entire focus is zero-cost abstractions.
- piva00 6y agoBecause for a long time people were trying to shove Go into use cases that before were covered by C applications. Some infrastructure has been developed in Go (Kubernetes being quite prominent) and so the overlap between systems programming and web/enterprise got muddy. Rust can't cover the enterprise use-cases of Go the same way that Go won't ever cover what Rust can do on systems/embedded level but there is enough overlap in some cases to confuse people into trying to compare them directly.
- pjmlp 6y agoYou mean like the USB Armory security key running Go bare metal? https://www.f-secure.com/en/consulting/foundry/usb-armory https://www.f-secure.com/en/consulting/foundry/usb-armory
- dx034 6y ago> Fun fact: because json is a reflective package in Go, solving your problem is quite trivial: https://play.golang.org/p/-TZ5b9Su1or https://play.golang.org/p/-TZ5b9Su1or . If I understand it correctly, your code doesn't check when unmarshalling if the content of Data has the expected structure. Using interface{} it's always been possible to achieve something like you'd do with Generics but it's ugly and can't check types at compile time.