-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: math: add Bool(bool) int #61915
Comments
We can't infer the type argument, and I would imagine that
That will save people from writing |
Same reflexion as @ianlancetaylor and I also find the function name confusing. |
@ianlancetaylor you're probably right but just because it's not inferred now doesn't mean it will never be inferred. If inference is later changed so that the generic version could be used like var x int64 = math.Bool(p)
n := x * math.Bool(q) the non-generic version would still need to be used like x := int64(math.Bool(p))
n := x * int64(math.Bool(q)) Even with maximal inference I imagine there would still be times where you'd need to write the type. This is most useful as part of another expression so I'd have to assume that there'd be enough type information the majority of the time in practice. I wouldn't mind adding the type even if it's most often int but it would be nice to have to do it less and less as inference improves. Even today I'm sure you could find cases where it would be handy to have the generic version like passing it to something like func f[S, T any](x S, y T, f func(S) T) S I'm not sure what the best long term decision is but I'd hate to sacrifice it to what's optimal now. That said, I'd be fine if it were int only. That would probably be good enough most of the time. |
@jimmyfrasche since it's a function call, the general handling of inference would require interprocedural analyses (the inferred type can depend on the call site which can be found at an arbitrary depth) Even without that, I'm not sure inference will work for every calls. I'd tend to think that int would suffice. |
@atdiar consider the example of
From that we can conclude that T0 = T1 for the program to be well typed. Whether that can be computed efficiently and whether it gets implemented are different questions but it's not anything fancy. Certainly that doesn't mean every case imaginable could be inferred but my assertion is that most calls would be within an arithmetic expression so if those are supported that should cover a lot of the uses. |
I see what you mean. But then for comparisons, if math.Bool(p) < math.Bool(q){...} Wouldn't work, no? (*using the math.Bool name although I still think that it is confusing since it doesn't return a boolean) |
Maybe |
The other idea is to have package bools, something like func Int(bool) int // 0 or 1
func Compare(bool, bool) int
func Xor(bool, bool) bool
func Xnor(bool, bool) bool |
the name: unimportant for now. No point in finding the perfect name if it doesn't happen. It's a placeholder. Feel free to continue to discuss it but I am purposefully ignoring my impulse to engage for now. @atdiar you are correct that would always require a type to be specified for at least one of the calls. re A package is interesting. It could contain |
Maybe |
Should probably take an iterator or at least have iterator variations. Similarly everything should all work on |
I'd argue against this being a function. Rather, this should be a native conversion operation supported in Go (see #45320). I'm trying to create a Go constant that's a static function of some numbers and a bool. Having this be a function makes it impossible to use in crafting a Go constant. The following would not be possible with this proposal: const GPSEnabledMask = math.Bool(GPSEnabled) << 17 |
I would much rather this be a conversion but there's a long history of being against that so I filed this |
As a practical matter, you can go in the opposite direction and define the GPSMask as a constant and then derive GPSEnabled as mask != 0. It does seem odd that you can only go in one direction. |
@carlmjohnson You can overload a mask with several meaning based on the constant information. E.g:
Then if |
Does this reflect Xor? X != Y and this reflect Xnor? X == Y If so I see no point to introduce syntactic sugar. This will harm readability because of inconsistent writing possibilities. |
Xor is |
I agree that this annoying to write and read. Do I miss something here? package main
import "fmt"
func main() {
a1 := false
a2 := true
b1 := false
b2 := true
fmt.Println(a1 != b1)
fmt.Println((a1 || b1) && (a1 != b1))
fmt.Println()
fmt.Println(a1 != b2)
fmt.Println((a1 || b2) && (a1 != b2))
fmt.Println()
fmt.Println(a2 != b1)
fmt.Println((a2 || b1) && (a2 != b1))
fmt.Println()
fmt.Println(a2 != b2)
fmt.Println((a2 || b2) && (a2 != b2))
fmt.Println()
fmt.Println(a1 == b1)
fmt.Println((a1 && b1) || (!a1 && !b1))
fmt.Println()
fmt.Println(a1 == b2)
fmt.Println((a1 && b2) || (!a1 && !b2))
fmt.Println()
fmt.Println(a2 == b1)
fmt.Println((a2 && b1) || (!a2 && !b1))
fmt.Println()
fmt.Println(a2 == b2)
fmt.Println((a2 && b2) || (!a2 && !b2))
} |
|
Yeah sorry, I was thinking about cases of non-booleans, where there are more than two values so you can't test it that way. |
What I wrote in #61643 (comment) still applies: if you can gather evidence that this is an important conversion, then probably a direct language conversion is the answer. Otherwise we shouldn't do this either. |
I created a little tool to help gather data: https://github.com/jimmyfrasche/issue61915 It's a command line programs that takes standard go tool patterns as arguments. It searches for
It logs a file position for each hit to stderr then shows a summary for each package on stdout. For each package with hits, it shows counts for the implicit (if-else) and explicit (func/map) hits then their sum. If more than one package is selected it shows a total broken down the same way. The if-else heuristic isn't perfect but it looks like it works fairly well in practice. It does overcount but the program itself undercounts as there are more esoteric ways to formulate things and there will be plenty of times the logic gets mixed in with more complicated situations The explicit counters are far more exact, unless the function is operating on some hidden state like an RNG or closed over IO or something. |
This proposal is a duplicate of a previously discussed proposal, as noted above, |
Change https://go.dev/cl/550235 mentions this issue: |
Helper to convert boolean expressions to numeric types under the standard
false = 0
,true = 1
mapping.where
number
is replaced by the constraint for all numeric types, wherever that ends up and whatever it ends up being named.As inference improves, the type should need to be specified less often.
I don't think the name is too important at this point. The important parts are is this the right thing in the right place. Names can be discussed once those are decided.
A language change to allow direct conversion from a boolean expression to a numeric type has been rejected several times, #9367 and #45320 . This is the next best thing.
This has been forked from discussion in #61643.
The text was updated successfully, but these errors were encountered: