Closed rsc closed 11 months ago
as soon as you have two different generic types to make zeros for now you have to write things like
var zeroK K var zeroV V return zeroK, zeroV, false
And to me at least that makes clear we have a problem. We can disagree of course, but most people I've spoken to about this believe there's a problem worth solving here: users shouldn't have to puzzle through one or more of these workarounds.
It's a bit cumbersome, but I'd just write it and then forget about it and get on with my life (but that's just my personal tolerance). Generic functions are only a fraction of all Go functions being written, and generic functions that need to create generic zero values are a fraction of generic functions, and those with more than one type parameter even more so. Also, the zero value creation part can already be implemented as a library function today.
So the only part of the proposal that brings something interesting to the table is the comparison aspect.
@rsc
A few people, including @AyushG3112 and @DeedleFake, https://github.com/golang/go/issues/61372#issuecomment-1636993251 https://github.com/golang/go/issues/61372#issuecomment-1636994974 again at iszero, but to me that's a non-starter. We already have a way to test for equality to a zero value for many types, and that's == 0 or == nil. To fit into the existing Go language, any solution here should look like those.
Just to add another option to the mix: It would be possible to expand nil
but only to comparisons. That is, we could say "x == nil
is always allowed and is true, if x
is a zero value". That would address the same problem as iszero
and would look like the existing ways. It also would not introduce another predeclared identifier and it would avoid the type-checking pitfalls of making nil
assignable to anything.
The downside is that you could then write x == nil
even if x = nil
would be invalid. It also does not address construction of zero values.
(Yet Another Compromise would be to expand comparison to nil
, but only to type parameter typed variables, which is the only place where it is not yet possible to write anything)
Instead of focusing on the upsides/downsides of expanding nil, what are the downsides of adding zero
? It’s a new predeclared identifier (con). It can be used in confusing ways where nil is more appropriate (con). Are there any other cons?
Rather than adding an identifier for zero value, does it make sense to add a "comparable to nil" constraint for slices, channels, maps etc?
That'll allow something like this to work, and act as a counterpart to comparable.
func isZero[T comparable | nilcomparable](val T) bool {
nc, ok := any(val).(nilcomparable)
if ok {
return nc == nil
}
var zero T
return val == zero
}
@AyushG3112 This is not going to work with custom struct types (with slices, maps etc. fields)
@AyushG3112 interface{ comparable | nilcomparable }
is not legal, as you can not have comparable
in a union (and nilcomparable
would likely have the same problems comparable
has for that). The reasons are complicated, but it's not something we're going to fix in this issue. Also, the .(nilcomparable)
wouldn't be possible just like .(comparable)
is not possible, as neither of those are types… well, really, there are many reasons that wouldn't work.
@Merovius @mateusz834 yes I know, what I'm saying is whether we can add support for this in the language over the zero identifier? Was just a suggestion.
@AyushG3112 And what I responded is "no, we can't for a bunch of reasons, among them the fact that comparable
can not happen in a union". Those reasons are complex. We won't be able to litigate them out of existence as a side-note in this issue.
I don't mind the idea of a "zero" identifier as long as it's constrained to comparison. I just don't like the idea of being able to return it or use it as an actual value because
return time.Time{}
and return zero
will now be equivalent with no proper language guidelines on what is idiomatic and why.I don't mind the idea of a "zero" identifier as long as it's constrained to comparison. I just don't like the idea of being able to return it or use it as an actual value because [...]
@AyushG3112
In principle I agree with that, but unfortunately it would probably mean that every month someone not knowing the context of this decision would create an issue asking to make zero
also usable as an actual value, because of the dangling carrot. It would require at least a FAQ entry with an explanation. The problem probably wouldn't exist with iszero()
, but I agree with Russ that if v == zero
reads better than if iszero(v)
.
@Nasfame consider var x struct { f, g func() }
No generics involved but there is currently no way to check if x
is its zero value currently aside from x.f == nil && x.g == nil
. Under the proposal you could simplify that to x == zero
.
Also every case where this is useful in generic code holds for generated code where the code generated doesn't involve generics. Being able to always write return zero, zero
lets a code generator not have to worry about a lot of special cases.
I disagree. I use zero checks and non-zero checks very often that I occasionally messes up
if err==zero
andif err!=zero
.
Doesn't that mean that you have a general difficulty with equality / inequality in C-style programming languages?
My point is that equality operators are susceptible to typos
Many things in programming are susceptible to typos. If this was a huge problem we should have different operators or no operators at all. Anyways, this is getting off-topic.
I personally would not want to see people writing if !iszero(err)
instead of if err != nil
in Go. This possibility is a reason against iszero()
in my book. I hadn't thought of that before.
Unnecessary uses of iszero(x)
or x == zero
/return zero
/etc. are all completely trivial things to lint. It would be very easy to mechanically enforce only using these where they are the only option.
consider
var x struct { f, g func() }
No generics involved but there is currently no way to check ifx
is its zero value currently aside fromx.f == nil && x.g == nil
. Under the proposal you could simplify that tox == zero
.
Memoization or some similar things jumped to mind, it's narrow but in the right spots a huge win - I think x == zero
would be really helpful for things like this.
what's wrong with that? Is it the flexibility ?
The addition of another way to do the same thing and, if someone actually does this, the break with an existing established pattern. I'm also convinced that someone who mixes up !=
and ==
would also mix up !iszero()
and iszero()
, especially if applied to error checks, because the convention of the unhappy path in the if
branch would require !iszero(err)
, not iszero(err)
.
what's wrong with that? Is it the flexibility ?
All codebases use err != nil
as an idiom for checking errors. Bringing in a different way is dissonant to long time gophers and at best confusing for new gophers learning the language as there would be two different ways of doing the same thing.
That said, I think it's very unlikely that people would start using !iszero(err)
since most IDEs have static analyzers that could warn against this usage favoring the all too established err != nil
.
I could write func an(err error) bool { return err != nil }
and then use if an(err) {
all over my code.
I could write
func no(err error) bool { return err != nil }
and then useif no(err) {
all over my code.
Actually your implementation should be named thereIsAn
.
lol, oops. The dangers of Sunday-posting!
Can I kindly suggest to let this issue cool down for a couple of hours? I don't believe this back-and-forth is going to change many minds and it is prone to sniping, repetitiveness and moving off-topic.
I don't understand the paranoid regarding the possibilities to do the same thing in slightly different manners. While it is slightly inconvenient in the presence of inconsistencies, the differences among x == 0
, x == zero
or x == nil
is the least thing to be worried about. They are just different names to the exact same thing, they are exactly the same, just like I can declare a variable with any name I like and it would not in any way raise the question of why this variable is named indices
and not indexSlice
. Languages are powerful, there are always different ways to do the same thing in any programming language, or else it will be useless. In Go we have math.Min
and built-in min
to get the minimum of 2 floating point numbers with mostly-the-same-but-slightly-different semantics, var zero T
, *new(T)
or even a custom-made zero[T]()
to get the zero value in a generics-compatible way, etc.
Regarding this specific proposal, the most important point being addressed is the ability to compare any variable against its type's default value in a generics-compatible manner. If the construct is not allowed to appear in non-generics code, then the restriction itself would be an inconsistency, and the simple mental model of generics being a copy-and-paste solution for parametric polymorphism is violated. On the other hand, if the construct is allowed in non-generics code, then it would inevitably lead to different ways to compare a variable against zero, regardless of the construct being a == nil
, a == 0
or isZero(a)
.
In conclusion, my point is that there would inevitably be inconsistencies if we want the most important issue to be addressed, but different spelling of the default variable is the most minor thing that needs to be concerned with and it should not interfere with the decision of including this feature in the language.
I'm wholly favor of this proposal, @rsc is spot on when he says:
the zero value is an important concept in Go that programs currently have no name for. Now they would: zero.
Full disclosure, I'm the author of one of his linked related proposals (https://github.com/golang/go/issues/60695).
Given the choice between:
zero
that is a superset of nil
nil
to be defined as the untyped zero value for any typeThe second option makes more sense because it simplifies the language, which I’d argue is in the spirit of Go. Given that nil
already means the zero value for non-pointer types, the author or reader would not have to consider whether the type is pointer-ish when assigning or comparing to the zero value.
Sure, var s string = nil
feels weird today, but does it feel more weird than uppercase exported symbols or gofmt
? Would it feel weird in 6 months or a year?
Would it make sense to try and distinguish zero from nil
instead of making it a superset of nil? That zero
is for zero-values of non-pointer types: structs, ints, floats, complex, strings, bools, and so on. Therefore if assigning it to a pointer type, it would create a new zero non-nil pointer?
Ie:
var x int = zero // x == 0
var x *int = zero // x == new(int)
It's not so much that I am convinced that this is a great idea, but that the only issue I see with this proposal is the overlap between zero and nil. This is just an attempt at preserving as much orthogonality in the language design as possible.
This would ensure that zero values are non-nil.
The big downside would be that the following would be confusing:
var x, y *int = zero, zero
x == y // false
Just food for thought.
"the zero value is an important concept in Go that programs currently have no name for", at least in the general case. @gophun seems to be arguing that maybe it shouldn't be such an important concept.
I didn't mean to dispute that the zero value is an important concept in Go. The zero value as a default for variables is great to avoid questions about uninitialized memory, and the rule to make the zero value useful is great, too. Using it as a throwaway value in return SomeStruct{}, err
is fine, too.
But what bothers me is giving special meaning to the zero value in the form of conditions based on it. The signifier of meaning should be something else, such as the ok
in the "comma ok" idiom, or the err
in return SomeStruct{}, err
, not the zero value.
I keep seeing statements that the primary goal is comparisons, and the suggestion to just add an iszero builtin. But can I ask, why then can't we consider allowing "truthy" tests to be a shorter acceptable equivalent? Instead of x == zero
or iszero(x)
, why can't we just allow all types to do if x
(not zero value) and if !x
(zero value).
I keep seeing statements that the primary goal is comparisons, and the suggestion to just add an iszero builtin. But can I ask, why then can't we consider allowing "truthy" tests to be a shorter acceptable equivalent? Instead of
x == zero
oriszero(x)
, why can't we just allow all types to doif x
(not zero value) andif !x
(zero value).
See https://pkg.go.dev/github.com/carlmjohnson/truthy#example-Value 😃
See https://pkg.go.dev/github.com/carlmjohnson/truthy#example-Value 😃
Right, so that exists via library. And if we are talking about the option of adding zero
or iszero()
to achieve the same result of testing for the zero value, why can't we consider making the feature of your library a builtin language feature so we can directly test values without the library?
I might miss it in the thread or from the description.
But since zero
value will be a universal value for any type, what will we see in this use case?
package main
import "fmt"
func main() {
val, err := returnZero()
fmt.Println(val == zero, val == nil, err == zero, err == nil)
}
func returnZero() (any, error) {
return zero, nil
}
I would expect to see true
for all of these conditions.
Could someone clarify please, what do we expect to see there?
Would it make sense to try and distinguish zero from
nil
instead of making it a superset of nil? Thatzero
is for zero-values of non-pointer types: structs, ints, floats, complex, strings, bools, and so on. Therefore if assigning it to a pointer type, it would create a new zero non-nil pointer?Ie:
var x int = zero // x == 0 var x *int = zero // x == new(int)
It's not so much that I am convinced that this is a great idea, but that the only issue I see with this proposal is the overlap between zero and nil. This is just an attempt at preserving as much orthogonality in the language design as possible.
This would ensure that zero values are non-nil.
The big downside would be that the following would be confusing:
var x, y *int = zero, zero x == y // false
Just food for thought.
That will kill the ability to use this in generic code.
BTW I think adding zero identifier would break code like this:
var zero int64 = 0
objectMeta.SetDeletionGracePeriodSeconds(&zero)
k8s API objects have a lot of pointer fields and &zero
is used for initialization
@AlexanderYastrebov thankfully a "predeclared identifier" is different in Go than a keyword, in that it is valid to shadow a predefined identifier.
Your example would therefore still compile and have the same meaning as before, but it would mean that for the remainder of this lexical scope zero
has a different meaning than what's defined in the specification (it's now literally int64(0)
), which could be confusing for maintainers of that particular function.
I wonder if the compiler could have a special diagnosis for a situation where assignment type checking fails for an identifier named zero
that isn't the predeclared identifier, explicitly noting that zero
was redefined with a definition location included in the error message.
@AlexanderYastrebov thankfully a "predeclared identifier" is different in Go than a keyword, in that it is valid to shadow a predefined identifier.
Your example would therefore still compile and have the same meaning as before, but it would mean that for the remainder of this lexical scope
zero
has a different meaning than what's defined in the specification (it's now literallyint64(0)
), which could be confusing for maintainers of that particular function.I wonder if the compiler could have a special diagnosis for a situation where assignment type checking fails for an identifier named
zero
that isn't the predeclared identifier, explicitly noting thatzero
was redefined with a definition location included in the error message.
go vet
could probably mark this code, but the compiler doesn't do anything about redefining len
or any
, so I don't see any reason for doing something else here.
@aohorodnyk I agree that all of those conditions should be true.
@justinfx
I keep seeing statements that the primary goal is comparisons, and the suggestion to just add an
iszero
builtin. But can I ask, why then can't we consider allowing "truthy" tests to be a shorter acceptable equivalent? Instead ofx == zero
oriszero(x)
, why can't we just allow all types to doif x
(not zero value) andif !x
(zero value).
I'd say the same arguments as against expanding nil
to be assignable/comparable to any type apply, just more.
Also, FWIW: I wouldn't say that allowing comparisons is the primary purpose of this proposal (though I agree, obviously, that many people say that). It's just the only thing that has an objective value, in that it allows to write code that is not currently possible to write. The other purposes - decrease in verbosity, having a clear and universal name to refer to an important concept - are subjective and harder to quantify, but that doesn't make them less important. I'd say.
will this work for
zero
as currentlycomparison operators
are only supported forcomparable
typesfunc SomeGenericFunction[T any](P T) bool { var z1 T = zero return P == z1 }
No - while P
is a variable that comparable with untyped zero
, z1
is an instantiated variable of type T
and those are not comparable unless specifically marked as comparable
in generic code.
@ianlancetaylor I think the behaviour of
zero
can be replicated with thisfunc zero[T any]() (zero T) { return }
No - since this code will not work:
func compareToZero[T any](val T) bool {
return val == zero[T]() // this will not compile
}
will this work for
zero
as currently==
is supported only forcomparable
typesfunc SomeGenericFunction[T any](P T) bool { var z1 T = zero return P == z1 }
It related to my question.
I might miss it in the thread or from the description.
But since
zero
value will be a universal value for any type, what will we see in this use case?package main import "fmt" func main() { val, err := returnZero() fmt.Println(val == zero, val == nil, err == zero, err == nil) } func returnZero() (any, error) { return zero, nil }
I would expect to see
true
for all of these conditions.Could someone clarify please, what do we expect to see there?
In the proposal we see:
Referring to a zero value in generic code. Today people suggest *new(T), which I find embarrasingly clunky to explain to new users. This comes up fairly often, and we need something cleaner.
zero
will return new(T)
then in case of reference it won't match.zero
will return nil
it will match (see my question).Looks like it's valid and open question about the behavioral.
- If
zero
will returnnew(T)
then in case of reference it won't match.- If
zero
will returnnil
it will match (see my question).
zero
will return *new(T)
and it will match, just like it does today https://go.dev/play/p/v93_g19Aj9p
will this work for
zero
as currentlycomparison operators
are only supported forcomparable
typesfunc SomeGenericFunction[T any](P T) bool { var z1 T = zero return P == z1 }
No - while
P
is variable that comparable with untypedzero
,z1
is an instantiated variable of typeT
and those are not comparable unless specifically marked ascomparable
.@ianlancetaylor I think the behaviour of
zero
can be replicated with thisfunc zero[T any]() (zero T) { return }
No - since this code will not work:
func compareToZero[T any](val T) bool { return val == zero[T]() // this will not compile }
You stick with T any
part where you are right. I thing the question should be focused on top of this part of the proposal:
Referring to a zero value in generic code. Today people suggest *new(T), which I find embarrasingly clunky to explain to new users. This comes up fairly often, and we need something cleaner.
package main
import "fmt"
func main() {
test[int]() // true
test[*int]() // true
}
func zero[T comparable]() (res T) {
return res
}
func test[T comparable]() (res T) {
fmt.Println(zero[T]() == res)
return res
}
package main import "fmt" func main() { test[int]() test[*int]() } func zero[T comparable]() (res T) { return res } func test[T comparable]() (res T) { fmt.Println(zero[T]() == res) return res }
This example doesn't work for type mystruct struct{ slc []int }
- it will simply not compile. The proposal does.
@Nasfame For your question about var z1 T = zero
: I assume the spec-change will do the same as for comparison against nil
: The spec will talk about one of the values in a comparison being "the predeclared identifier zero
" and allow that. Consequently it will reject your code, as it doesn't use the predeclared identifier zero
. That is the same as this code being rejected currently:
func main() {
var f func() = nil
fmt.Println(f == nil) // works
fmt.Println(f == f) // doesn't work
}
@aohorodnyk
I thing the question should be focused on top of this part of the proposal
I'd refer to what I said here: The proposal addresses multiple things with a single mechanism, none of which is the "primary" or "only" thing it does. I think it's important to acknowledge that and that an alternative that only solves one of them - no matter which one it is - has this disadvantage over the proposal.
That being said, I also disagree that your code
func zero[T any]() (v T) { return }
really does replicate this part of the proposal. In particular: In the language as it is today, the type argument for zero
is not being inferred. So you'd have to write return zero[time.Time](), err
, which is worse than return time.Time{}, err
.
Of course we might at some point change the language to make type inference more powerful and infer type arguments from assignment context. At that point, yes, that function would replicate that part of the proposal.
Though note that you'd still be stuck on having to declare func zero[T any]()
somewhere. If it is a predeclared function, then what's the benefit over the proposal? If it is in the standard library, users would have to write return somepkg.Zero(), err
, which is again as clunky as return time.Time{}, err
. If it isn't defined anywhere, users would have to declare it themselves, which is boilerplate.
Note that the primary argument behind the predeclared min
/max
functions was that they are commonly used, so shouldn't require an import. This applies even more so to zero
, I'd argue.
So, in summary
@Nasfame As I said above, the likely way this would be put into spec is "as a special case, any value may be compared against the predeclared identifier zero
and evaluates to true if it is the zero value of its type". This is analogous to how nil
works today:
Slice, map, and function types are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier
nil
. Comparison of pointer, channel, and interface values tonil
is also allowed and follows from the general rules above.
func main() {
var f func() = nil
fmt.Println(f == nil) // works
fmt.Println(f == f) // doesn't work
}
@Nasfame x == zero
does not resolve to x == MyStruct{}
@Nasfame I disagree with the distinction you are making. In particular, your assertion that x == zero
"resolves to x == mystruct{}
is directly counter to what I said above how that comparison would likely be defined in the spec - which is exactly what x == nil
does.
Either both "resolve to a value", or neither does. There is no basis for the distinction that you are making, except that you choose to think about it that way.
I don't think it is helpful to further discuss this, so I at least will duck out of this particular thread of discussion.
@Nasfame var x MyStruct = zero
and var x MyStruct = MyStruct{}
are 2 different ways to do the same thing, they do not necessarily resolve to each other.
zero
- can be treated as auntyped constant
.
No, it can not. The proposal explicitly says so. You can not write 1+zero
and you can not write const x = zero
. zero
is a special predeclared identifier, just like nil
.
If we permit scenario-2, what exactly is
zero
then?
The predeclared identifier zero
. What is nil
if you write f == nil
? It can't be a function variable with value nil
, as function variables are not comparable. You wouldn't say that "in f == nil
, nil
is (func())(nil)
, so the comparison should be invalid". So stop trying to argue that in x == zero
, zero
should be mystruct{}
, so the comparison should be invalid.
Please try to understand how zero
and nil
are similar, instead of trying to force them to be understood to be different. The differences you are trying to ascribe to them are directly contradicting the intended semantics of the proposal.
If that's the case :=
var x *MyStruct=zero; x==x
should be possible
var x func() = nil; x == x
is not valid. So neither should this be.
Personally, I didn't find
nil
spec as complicated as this!
That is because you are trying to make zero
more complicated than it is. For reasons that are beyond me.
There is some speculation about what the spec would say. As noted in the top message, the proposed spec changes are at https://go.dev/cl/509995.
There is also some confusion about zero vs nil. To try to be very clear, zero would be exactly like nil except that it can be used to denote and compare for the zero value of any type, whereas nil can only be used for chan/func/interface/map/slice values. The relevant parts of the updated spec say:
In "Assignability", the text would read:
A value x of type V is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
- ...
- x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type, but not a type parameter.
- x is the predeclared identifier zero.
and
Additionally, if x's type V or T are type parameters, x is assignable to a variable of type T if one of the following conditions applies:
- x is the predeclared identifier nil, T is a type parameter, and x is assignable to each type in T's type set.
- x is the predeclared identifier zero.
- ...
In "Comparison operators", the text would read:
Slice, map, and function types are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier
nil
. Comparison of pointer, channel, and interface values tonil
is also allowed and follows from the general rules above.As a more general special case, any value may be compared to the predeclared identifier
zero
.
- For numeric types, comparison to
zero
is equivalent to comparison with 0.- For string types, comparison with
zero
is equivalent to comparison with the empty string.- For slice, map, function, pointer, channel, and interface types, comparison to
zero
is equivalent to comparison tonil
.- For struct types, comparison to
zero
is equivalent to comparing each struct field tozero
.- For array types, comparison to
zero
is equivalent to comparing each array element tozero
.
In all three snippets, the nil rule is unchanged from what it is today. I hope it is clear seeing the nil rule and the zero rule next to each other that in all cases the zero rule serves as a more relaxed form of the nil rule.
There is also confusion above about the distinction about *new(T), specifically misreading it as new(T) (note the star in the first one). Zero for a given T would be like *new(T). *new(T) is the clumsy kludge some people use today to the zero value for type T that happens to work for all T. That is, these two are exactly equivalent:
var x = *new(T)
var x T
Note that this is different from var x = new(T)
, which is always a pointer. The confusion about reading *new(T) as new(T) reinforces my claim that it is clumsy and awkward and we need something better.
There is also some confusion about zero vs nil. To try to be very clear, zero would be exactly like nil except that it can be used to denote and compare for the zero value of any type, whereas nil can only be used for chan/func/interface/map/slice values.
@rsc given this confusion—in your view, what’s the concise case against just using nil
instead of a new predeclared identifier zero
?
Thanks!
Hi @ydnar
@rsc given this confusion—in your view, what’s the concise case against just using nil instead of a new predeclared identifier zero?
If I understand your question, I think a concise response from Russ on that topic is in https://github.com/golang/go/issues/61372#issuecomment-1636929105:
- Re expanding nil, I think accidents like var x int = nil, or writing f(nil) when f takes an integer, or writing x == nil when x is an integer, or writing *x == nil when x is a *int but *x is an int, are mistakes that are worth continuing to diagnose. So I am not in favor of allowing nil to mean any zero anywhere. In C, NULL is defined literally as 0, and it is easy to make mistakes like that. In Plan 9 C our standard headers said
#define nil ((void*)0)
, and it caught plenty of mistakes where you wrote nil and should have written an integer; NULL would not catch those, and nor would an expanded Go nil. Types are good, and we should continue to take advantage of them.
edit: fixed markdown in the quote that was mangled by my copy/paste.
I propose to add a new predeclared identifier
zero
that is an untyped zero value. Whilenil
is an untyped zero value restricted to chan/func/interface/map/slice/pointer types,zero
would be an untyped zero value with no such restrictions.The specific rules for
zero
mimicnil
with fewer restrictions:zero
is assignable to any variable of any type T that does not already have a short zero (0, "", nil), including when T is a type parameter with constraint any.zero
when it cannot already be compared to a short zero (0, "", nil), again including when T is a type parameter with constraint any.That's it. That's all the rules.
Note that assignability includes function arguments and return values:
f(zero)
andreturn zero, err
are valid.See CL 509995 for exact spec changes.
This proposal addresses at least three important needs:
Referring to a zero value in generic code. Today people suggest
*new(T)
, which I find embarrasingly clunky to explain to new users. This comes up fairly often, and we need something cleaner.Comparing to a zero value in generic code, even for non-
comparable
type parameters. This comes up less often, but it did just come up incmp.Or
(#60204).Shortening error returns:
return zero, err
is nicer thanreturn time.Time{}, err
.More generally, the zero value is an important concept in Go that some types currently have no name for. Now they would:
zero
.Because zero is not valid anywhere 0, "", or nil are valid, there will be no confusion about which to use.
I'm not claiming any originality in this proposal. Others have certainly suggested variants in the past, in quite long discussions. I'm not aware of any precise statement of the exact rules above, but I won't be surprised if one exists.
A brief comparison with earlier proposals:
61129 proposes a builtin
iszero(x)
. This proposal usesx == zero
instead.60695 proposes a generic zero function:
zero(T)
is a zero of type T. This proposal uses plainzero
which enables use in comparison and avoids a usually unnecessary(T)
. In non-assignment, non-conversion contexts where a type must be written, this proposal usesT(zero)
, which seems more idiomatic to me at least.35966 proposes an untyped zero value spelled
{}
. Later discussion in also consideredzero
.19642 proposes an untyped zero value spelled
_
.