-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: Go 2: allow explicit conversion of bool to arbitrary numeric types #45320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Previous discussion at #9367 (and other discussions before that, I think). Not sure it has ever gone through the modern proposal process, though. |
I intentionally declined to propose converting integers to bools. I want to be able to bitwise-or bools and otherwise integrate them into integer expressions, but we already have |
One of the comments in it cited to the ambiguity of what bool(intValue) should mean as a weakness, which I agree it sort of is. |
Slightly off-topic but re the discussion in #9367, to be honest, the whole "true could be interpreted as both 0 and 1" argument seemed weak to me. The only example provided was exit statuses across operating systems, which is platform-specific anyways. For example, VMS treats 0 as a warning and 1 as a success, while POSIX treats nonzero as an error. Explicitly converting between the zero value of |
As far as I understand the proposal at least two reasons are given in favor of the proposal:
As noted in practice there likely isnt a performance benefit as the current compiler already optimises the existing pattern to convert a bool. If there is we can work on fixing those without changing the language. Alternative Suggestion:Instead of changing the language the std lib could contain a generic function that converts any integer type to bool and we make sure at least the Go GC implementation always inlines and optimises the |
I mean, it could be a function. but is there any reason for which, I dunno, The primary goal here is to make the code more expressive and allow short but still-reasonably-clear code, but also to hint that this shouldn't be a branchy operation. |
The difference is that a language change has a much higher bar to be acceptable then adding a new function in the std library. And a language change likely also doesnt have the upside to guarantee branchness as that is an implementation detail of the specific Go implementation. The main upside to making it a language change I can see currently then becomes it can potentially be shorter in writing.
Just because it is short and doesnt contain an |
I was thinking this while I read the thread. Having to explicitly call a |
I don't think it's useful to discuss performance in this issue. We are not going to make a language change of this sort for performance reasons. We would only make this change if it would provide a clear benefit for writing and, especially, reading Go code. |
This proposal does seem very similar to #9367 and #6011. What has changed since then? I'll note that it would seem odd to support converting from boolean to numeric but not support converting numeric to boolean. Are there any other one-way conversions in Go? (Other than converting to an interface type, for which the reverse conversion requires a type assertion.) |
@ianlancetaylor it makes it a lot easier to read branch free code, when you can use the normal algos instead of having to write things in asm. @mvdan that would be a bit of a pain to read the code. |
The compiler defines:
Which we then use lots of places. So although I think converting from |
@randall77 that defeats the purpose by introducing a branch (or similar flow control) and possibly a function call. |
@docmerlin Please don't confuse a branch in the Go code with a branch in the generated machine code. For example, when using the gccgo compiler, this Go function func Cmp(a, b int) int {
if a < b {
return 0
}
return 1
} compiles to this assembly code (ignoring the stack-splitting code):
|
For cryptographic code it would be great to have a function to go from Packages like filippo.io/edwards25519 have to use We'd probably also need the other way around, because those values are also generate in constant time by masking and bit shifting. |
Ohhh. I hadn't thought about the constant-time guarantee, but that does seem relevant. Although... Is the naive CPU code actually constant-time? I have no idea. It seems like it'd be nice to be able to drop the extra nop, although it probably doesn't matter much. |
This is a side comment, but the notion of guaranteed constant time in non-assembly code appears to require that the compiler never insert branches that aren't present in the source code. I doubt any compilers do that today but it's hardly impossible. |
Every cryptography engineer expects in fear the day that will inevitably happen, and secretly hopes to retire before then. There are occasionally rumors of LLVM-related sightings. A much more sinister prospect than quantum computers, which at least will require new exciting designs, it's considered indelicate to talk about it in polite cryptography circles. |
Well, now I know the next GCC optimization that I should work on! |
ಠ_ಠ |
FWIW, neither performance nor knowing about branching are my reasons for supporting this proposal. I just think that some algorithms are easier to read when, for example, using the truth value of some decision procedure later in some function isn't obscured by control flow. |
Understood. But it seems that |
Although it's been implied, I don't think anyone's explicitly said that the |
This is good to avoid custom |
What do people think of func AsInt[T constraints.Integer](b bool) T {
if b {
return 1
}
return 0
}
v := AsInt[int32](b) |
@ianlancetaylor Are generics really pulling their weight in that example? It seems to me that just returning func AsInt(b bool) int {
if b {
return 1
}
return 0
}
v := int32(AsInt(b)) |
@magical Fair point. |
Based on the discussion above this doesn't seem to rise to the level of an appropriate language change. Therefore, this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. |
In some cases, being able to do arithmetic or bitwise operations in which you can use a true-or-false value without a branch is highly appealing. It would be convenient to allow conversion of true and false to arbitrary integer types, producing the values 1 and 0 respectively. Conveniently, unless I get around to writing up my
int0
proposal and convince someone that it's a good idea, every numeric type in Go can represent both of those values.I've been told that the Go compiler is already smart enough to generate reasonable code for something like this:
However, even if the compiler can do it, it's a lot more code to read than something like
int(cond)
would be, and whether or not it's branchy is less obvious and I don't know whether it's safe to use this in code where a branch is Too Expensive. (Unfortunately, Google Groups has destroyed any once-functional Usenet searching, so I can't find the reference, but I seem to recall someone getting a 20% overall improvement in a PRNG by replacingif (z) { x += y }
withx += y * z
.)I originally strongly disliked the thing where true and false weren't numeric values, but I've come to quite appreciate the need to make a conversion between booleans and numbers explicit. But "explicit" need not mean "verbose". Allowing
int(a < b)
to be 1 or 0 depending on whether a was or was not (respectively) less than b seems like it would simplify a lot of code without hurting anything.The text was updated successfully, but these errors were encountered: