6 ms·
The predeclared identifier nil is a constant, like 0 or "". Like 0 or "", nil is untyped. Go has both typed and untyped constants. 0 also is untyped, which is
by neild 6y ago
The predeclared identifier nil is a constant, like 0 or "".
Like 0 or "", nil is untyped. Go has both typed and untyped constants. 0 also is untyped, which is why you can write x==0 regardless of whether x is an int32 or an int64.
Unlike 0 or "", nil does not have a default type. The default type of a constant is the type that it is implicitly converted to when a type is required. For example, "x := 0" declares a new value x with the value 0, but 0 is untyped. Untyped integer constants have a default type of int, so x is given the type int.
Since nil does not have a default type, it's an error to write "x := nil".
You can declare a typed constant with the value 0 or nil. int32(0) is a constant with type int32 and the value 0. (* int32)(nil) is a constant with the type pointer-to-int32 and the value nil.
Variables of an interface type can hold any value that satisfies the interface. For example, a value of type io.Reader can hold any value that has a Read method with the appropriate signature.
The common confusion about nil occurs because you can compare an interface value to a non-interface value.
var b *bytes.Buffer // b is a variable with type *bytes.Buffer
var r io.Reader // r is a variable with type io.Reader
r = b // r now holds a value with type *bytes.Buffer and value nil
b == nil // true: b is nil
r == b // true: r and b are equal
r == (io.Reader)(nil) // false: r is not a nil io.Reader
r == (*bytes.Buffer)(nil) // true: r contains a nil *bytes.Buffer
r == nil // false: r is not a nil io.Reader
The confusing part is that last line. How can r not be nil, when it contains a nil value? And the reason is hopefully apparent from the previous two lines: It depends on the type of "nil".
Whether you find this a wart in the language or not, this is not a case of nil being either typed in theory or untyped in practice. The predeclared identifier "nil" is an untyped constant.
- mh- 6y agoexceptionally clear explanation, thank you.
- morelisp 6y ago> Like 0 or "", nil is untyped. Go has both typed and untyped constants... Unlike 0 or "", nil does not have a default type. So it feels like you're trying to draw some distinction here that is like, "look, nil isn't really that special - there's groups A and B, and the core confusion is people think nil is in group A while the truth is in it's group B." And it's true, Go has both typed and untyped constants. But as far as I know, nil is the only untyped constant without default type. So "group B" is still just nil, and nil remains a sui generis source of confusion in Go.
- 13415 6y agoYes, but giving nil a default type would be way more confusing.
- morelisp 6y agoGo already kind of does though, when you use it in a type switch nil has type nil. For binding purposes, `interface{}` also seems reasonable. (Or only half-joking, if I wanted maximum pragmatism for minimal clarity, nil's default type should be error.) It seems the root problem is that interfaces and concrete values occupy the same semantic positions. But that's way too hard a problem to fix; if I had a magic wand to update all existing code I'd probably just have `== nil` on an interface check for both kinds and try to rescue transitivity.
- brundolf 6y agoAt the risk of inviting a flame-war, it's distressing to me that a language this recent, with such a purported focus on simplicity, has design-problems this fundamental
- morelisp 6y agoSorry, you won't get much agreement from me on this. There's only two kinds of languages, the kind with fundamental design problems and the kind people don't use. If you are looking for the perfect language may I suggest Forth on a Z80? Otherwise there's going to be a pile of shit hiding somewhere.
- brundolf 6y agoI disagree. Issues like this on really core language primitives usually crop up when either: 1) The language lacked hindsight in its domain because there wasn't a ton of prior art (C) 2) The language didn't have enough foresight put into it (JavaScript) 3) The language's modern usage has drifted significantly far from its original usage (Java) Python is an example of a language people use that doesn't suffer from #1 (thanks to Perl and friends), doesn't really suffer from #2 (at least, Python 3) and doesn't (yet) suffer too badly from #3. It has plenty of flaws and limitations, but not in its foundation. The core language primitives are rock-solid. #1 doesn't apply to Go, and it's not old enough for #3 to apply. So that seems to only leave #2. (I should add that this doesn't make a language completely invalid or useless. I use (and even like!) JS despite its flaws; in fact I prefer it to Python overall, mostly due to the ecosystem. It's just disappointing to see issues of type #2, because they seem so avoidable.)
- stcredzero 6y ago0 also is untyped, which is why you can write x==0 regardless of whether x is an int32 or an int64... Since nil does not have a default type, it's an error to write "x := nil". But not an error to write "x := 0"?
- saghm 6y agoThe next paragraph they wrote explains this: "Unlike 0 or "", nil does not have a default type. The default type of a constant is the type that it is implicitly converted to when a type is required. For example, "x := 0" declares a new value x with the value 0, but 0 is untyped. Untyped integer constants have a default type of int, so x is given the type int."
- dhagz 6y agoBecause 0 has a "default type" of int. So when the compiler goes to determine the type of x, it sees its set to 0, which the compiler assumes to be an int. I've found thinking about nil too much in Go hurts or creates weird loops in my brain, so I just tend to worry about it when I'm dealing with interfaces or pointers. Everything else I'm looking at zero-values.
- stcredzero 6y agoI'd much prefer it if in golang, the type of nil was some "nilType" for which != and == is defined for all other types. Or, define some other operation, like isNil() Then determining if something is nil could be done without thinking about it at all. nil would be nil would be nil. (Also, it would be illegal to put methods on nilType, of course.) (Yes, I make my living writing go, and this would make my life easier. And no, in that case, don't allow x := nil.)
- masklinn 6y agoNo, because as they note constants can have a default type[0]. For integer constants, that's `int`. Meaning if a literal integer constant is not otherwise typed, it will default to `int`. [0] https://blog.golang.org/constants#TOC_5 https://blog.golang.org/constants#TOC_5.
- siebenmann 6y agoThe Go specification is careful to not call 'nil' a constant, and in fact at one point specifically says that it isn't ("Conversions", in the section on converting constants into typed constants, which actually uses '(*int)(nil)' as an example of something that is not a typed constant). Also, although it wasn't clear in the entry, the original article that it was a reaction to talked specifically about 'nil variables' (ie, variables with the value of nil). (Even the concept of 'the value of nil' is tricky in Go; I believe the specification only talks about things being comparable to nil or allowing nil to be assigned to them. The specification really goes to a lot of work to not treat nil as a value, exactly. I suspect that the Go spec authors really did not want a rerun of the C idea that the NULL pointer is '0' and has an all-zero value and so on.) (I'm the author of the linked-to entry.)
- yoneda 6y agoHuh, so equality isn't transitive in Go. Yikes.
- Spivak 6y agoEh, languages end up with this all the time with type conversions. In JS: false == undefined => false false == null => false undefined == null => true
- masklinn 6y agoFirst, your comment makes no sense: a = 1 b = 2 c = 1 a == b => false b == c => false a == c => true Is that surprising? Equality is transitive, inequality is not. Second, if you have to reach for old javascript "features" (or got forbid PHP) to defend a language you're in a bad, bad place. Third, the broken equality is not something that's usually praised about javascript. In fact the first rule of javascript equality is that there is only one situation in which `==` is the correct operator: checking that something is null (because that avoids having to differentiate between null and undefined, papering over one javascript wart using another). In every other situation you want "strict" (===) equality.
- virtue3 6y agoI managed to get my team to use `lodash.isNil` because we had gotten bitten by this crap too many times. And `lodash.isEmpty` for strings/arrays/etc.
- mirekrusin 6y agoWhat is absurd about this statement from logical point of view? A != B A != C B == C ...looks like valid logical statement, ie. A = 1, B = 2, C = 2: 1 != 2 1 != 2 2 == 2
- presto8 6y agoWhat about A=1, B=2, C=3? A != B 1 != 2 A != C 1 != 3 B == C 2 == 3
- esrauch 6y agor==b and b==nil but r!=nil seems like the kind of thing that people give JS a lot of crap over.
- mirekrusin 6y agoCan you please give more concrete example that is valid js but is absurd logically?
- camjw 6y agoI think the classic example is [] == ![] evaluates to true. It’s not absurd logically, just the operators act weirdly/non-standardly.
- mirekrusin 6y agoYeah, that one is funny > [] == [] false > [] == ![] true
- mirekrusin 6y agoHowever typescript says: This condition will always return 'false' since the types 'never[]' and 'boolean' have no overlap. ...for `[] == ![]`. Which is ironic because it does return true, not false. However at least it flags an error! I wonder how much of weirdness is wiped out by linters/ts/etc.
- qsort 6y agoHonestly that's more of a weirness of the '==' operator, which is kind of deprecated in modern JS. On the other hand I fully agree that non-strict definitions of equality are bound to generate weird cases around nullish values.
- esrauch 6y agoI don't actually subscribe to the "JS is a bad language" meme (and maybe the meme has died off a bit, partially thanks to typescript), but implicit coercion is one thing that people cite, with behavior exactly like that nontransitive Golang case. Also see this minitalk (which actually gets some things wrong because of confusion on how the terminal interpreter behaves but is still amusing): https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- deleted 6y ago[deleted]
- drvd 6y ago> How can r not be nil, when it contains a nil value? In the same way a slice can contain one or more nil values without itself _being_ nil. A gift box containing nothing is not itself nothing. > And the reason is hopefully apparent from the previous two lines: It depends on the type of "nil".It depends on the type of "nil". Well, no. "r == nil" tests whether r itself is nil (i.e. the zero value of that interface type) which it is not. It contains b and is thus no longer nil. The type doesn't play a (large) role here. This misconception might be due to the fact that lots of types can be nil: slices, maps, channels, functions and (unfortunately) interfaces. Nobody is astonished that a slice containing any sort of nils is not nil itself; that a buffered channel containing nils is not nil itself and that the constant function `func()*int { return nil }` is not nil itself either. Only for interfaces this poses a problem. If interfaces wouldn't be "nil" but lets say "zilch" than it would be evident that r != zilch.