9 ms·
Typed nils in Go 2
- nie 9y agoI enjoyed the blog article and I would like to gently reiterate the notion that a _typed nil_ in Go 2 would change the semantic of _nil_, as seen in the example expression at the end of the article: 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`
- dullgiulio 9y agoCan someone point me to some examples where checking that the type of an interface is nil? I have thought a bit about it but I couldn't come up with good situations.
- deleted 9y ago[deleted]
- gondo 9y ago"the type of an interface is nil" is that even possible?
- mbertschler 9y agoYes it is. An interface is basically a struct of the concrete type and the value. While the outer interface value still has a type, if no value was assigned to it, the concrete type field is still nil.
- TheDong 9y agoThat, in fact, is the normal case which is why people are surprised by typed nils. For example: func returnsNil() error { return nil } x := returnsNil() // x is of type nil and value nil.
- masklinn 9y agoYes. The interface holds the concrete type of the value, if there is no concrete type it will be nil, so if you assign a nil to an interface-typed variable directly, you'll have a (nil, nil). If you first assign the nil to a pointer type T then assign/convert that to an interface type, you'll get (* T, nil). Here's a trivial demo: var a interface{} = nil // (nil, nil) var b *int = nil var c interface{} = b // (*int, nil) fmt.Println(a == c) Of course most such cases are not that trivial, rather they're cases where a function takes an interface-valued parameter and checks for (param == nil), if the caller passes in an actual object there's no problem, if they pass in a concrete value no problem, but if they extract the nil literal to a concretely-typed context (variable) things go pear-shaped to various levels of fuckedness (depending what is done in the other branch). And that's vicious because something as seemingly innocuous as "extract variable" on an immutable literal can break your code.
- gondo 9y agothanks for the example. so the answer to @dullgiulio question could be done by using reflection: var a interface{} = nil // (nil, nil) fmt.Println(reflect.TypeOf(a) == nil)
- dullgiulio 9y agoThank you for your answer. I was writing from the phone and didn't make myself clear. My question is: what are the legitimate use cases for interfaces that are half-nil? Ignoring the compatibility guarantee for the sake of discussion, I feel that nobody would notice if the compiler tomorrow started short-circuiting the equality check of interfaces against nil to return true if either tuple value is nil. But maybe I'm missing some use-case.
- masklinn 9y ago> My question is: what are the legitimate use cases for interfaces that are half-nil? I think that's two different questions: * Is there a legitimate use case for nil not being nil? I don't think so. * Is there a legitimate use case for having "typed nil" interfaces? Kinda, Go supports and encourages calling methods "with nil receivers", and doing that through an interface requires that the concrete type (the non-nil half) be conserved otherwise you can't dispatch the method call.
- EdiX 9y agohttps://play.golang.org/p/rfq52ZmLPS https://play.golang.org/p/rfq52ZmLPS
- dullgiulio 9y agoMy question misses the important words "a situation where it makes sense." As I wrote below: Ignoring the compatibility guarantee for the sake of discussion, I feel that nobody would notice if the compiler tomorrow started short-circuiting the equality check of interfaces against nil to return true if either tuple value is nil. But maybe I'm missing some use-case.
- inopinatus 9y agoThe more experienced a developer I become, the more strongly (and negatively) I feel about nils and nulls and their ilk. I have sympathy for C.A.R.Hoare who in 2009 apologised for the apparent invention of null references in ALGOL W (1965), calling them a "billion-dollar mistake". I've come to regard them as a data singularity, and when I design data structures and interfaces today I am deliberately avoiding/outlawing them; all my relational fields are NOT NULL and I choose either meaningful defaults, or EAV or equivalents instead; in method parameters I would rather something not exist than for it to accept a null reference or value. And I believe that the resulting code is more modular, more easily refactored and more reusable a result, errors are better handled, and the resulting data structures and calling arguments more easily interpreted, more readily queried and destructured, and are (so far) proving generally better fitted to real-world domains.
- taeric 9y agoFunny, I'm the opposite. The more experienced I've become, the more I've found that nil-punning is ultimately what I actually wanted. 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.)
- nerdponx 9y agoI'm all for the idea that relational fields should be NOT NULL What if the data is actually missing? How else do you record that information?
- 9y ago
- heja_bruh 9y agodoes Go still takes a hit on GC or has that improved now? I wonder why people still aren't using non-GC languages...
- maerF0x0 9y agoIMO the language made a mistake by allowing nil to satisfy any interface . When I write a function like 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.
- tjholowaychuk 9y agowhat about `error`?
- Sharlin 9y agoEmulating the sum type val|err with a product type (val, err) - where val and err are implicitly nilable - is one of Go's biggest design smells in the first place.
- tonyedgecombe 9y agoYou can fix that with exceptions :)
- tjholowaychuk 9y ago:'(
- deleted 9y ago[deleted]
- TheDong 9y agoAllow an Either monad via allowing sum types, solved. 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.
- 9y ago
- pyrale 9y agoGotta love how the initial problem (a typed language in the 2000s having nil and empty interfaces) degenerates in workaround suggestions such as hacking nil and the interface system to give it a special type.
- ZGF4 9y agoIt is interesting how Go seems to be doomed to head down the same path as other languages it accused of being "bloated." No one intends to design a complicated language but the reality is that the problem space is complicated. 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.
- camus2 9y ago> It is interesting how Go seems to be doomed to head down the same path as other languages it accused of being "bloated." No one intends to design a complicated language but the reality is that the problem space is complicated. 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.
- hoodoof 9y agoThis may be the "computing industry sentence of the year", if they had an award for "sentence of the year", which I'm sure they don't. Whoever they are. 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.
- _pmf_ 9y ago"Go should stay simple, it does not need generics."
- concede_pluto 9y agoThis is required to support one of the most peculiar features I've ever seen--method dispatch on the concrete type of object that isn't there.
- wvh 9y agoThis is where language design morphs into philosophy...
- knocte 9y agoYet another item to add to my list-of-reasons-of-why-not-to-use-Go. Thanks
- IshKebab 9y agoIf you don't use languages because they have some edge cases and minor flaws how do you do any programming at all?
- Gigablah 9y agoYou don't need to do any programming in order to make snarky comments on HN.
- coldtea 9y agoDoes that sound like an edge case?
- laumars 9y agoChecking the value of a property [edit: return of method] after you've nil'ed the parent object is enough raise an exception in most languages. So yes I'd say that's an edge case. Where Go gets it wrong here is because nil isn't really `nil` you get a silent `false` rather than an obvious crash + stack trace. But regardless of the bad design of Go around the usage of "nil", the code would have failed in pretty much any other language anyway.
- masklinn 9y agoYou're not "checking the value of a property after you've nil'd the parent object", you're checking if you were given a nil. This issue can occur for any function which takes an interface-typed parameter. That's usually how it happens: somebody passes in a `nil` which comes from a pointer-typed variable: https://play.golang.org/p/ADTvLDDrw6 https://play.golang.org/p/ADTvLDDrw6 > But regardless of the bad design of Go around the usage of "nil", the code would have failed in pretty much any other language anyway. No, it would not. In Java, null is null whether it's typed as a concrete reference, as an array or as an interface.
- lngnmn 9y agoOh, that is truly hipster's concept - more than one nil. similar to -0 and +0. Public cosplay of [presumed] intelligence as a new smoking.
- fithisux 9y agoI didn't know that you can compose a struct with an interface. Nice.
- smegel 9y agoGood example of code smell.
- mrkgnao 9y agoGo is a lesson in how complexity can't be eliminated, only distributed properly from the beginning so that one doesn't have to hack it in later with messy special-casing that needs you to know how the compiler represents things under the hood. What happened to "lightweight typesystem that reduces cognitive load"?
- ghthor 9y agoIt's really easy to criticize where mistakes were made. The intention was to make a simple language and it worked. The idea resonates with many many engineers even ones such as I that love writing powerful pure fn code. 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.
- mrkgnao 9y ago> Go is an open language 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.
- pjmlp 9y ago
- BlaXpirit 9y agoCrystal programming language has solved the problem of nil by making it its own type and supporting ad-hoc union types. https://crystal-lang.org/api/Nil.html https://crystal-lang.org/api/Nil.html https://crystal-lang.org/docs/syntax_and_semantics/union_types.html https://crystal-lang.org/docs/syntax_and_semantics/union_typ...
- heythere124 9y agoPython does that too.
- junke 9y agoCommon Lisp: http://clhs.lisp.se/Body/t_nil.htm http://clhs.lisp.se/Body/t_nil.htm, http://clhs.lisp.se/Body/t_null.htm http://clhs.lisp.se/Body/t_null.htm
- BlaXpirit 9y agoI thought it did not need to be mentioned, but dynamically typed languages don't count for this comparison. Every value is like a union of every type, and compile time type checks are impossible.
- junke 9y agoWhy would a language define an empty type if not for static type checking?
- Veedrac 9y agoI'm confused. Nil isn't an empty type. Why are you introducing them?
- junke 9y agoIt goes like this: the claim is that dynamically typed "don't count for the comparison" because "compile time type checks are impossible". Even if we suppose that nil values or union types are useless at runtime (they aren't), it is not true that dynamically typed languages could not be analyzed statically. Not only compile time type checks are possible, they are sometimes expected to happen and already part of some language's design. Python added type hints recently, but in Common Lisp, there is not only a null type (which contains the nil value), but also an empty type: there is no practical use in defining a type for which there is no possible value at runtime, except if the language and its type system are designed to support static analysis. It it expected that a compiler can optimize away things that are known in advance to be impossible, or help you detect errors statically.
- deleted 9y ago[deleted]
- deleted 9y ago[deleted]