proposal: support int(bool) conversions in Go.1.5

1,406 views
Skip to first unread message

adon...@google.com

unread,
Dec 17, 2014, 2:33:17 PM12/17/14
to golan...@googlegroups.com
I propose the following modest language change for Go 1.5.  (This is issue #9367.)

An explicit conversion int(b) where b has a boolean type would become legal, and would yield 1 if b is true, 0 otherwise.  Similar conversions would be legal for all numeric types.

Rationale: currently this function cannot be computed in a single expression, despite it being often useful, hard to misuse, efficiently implemented by modern ALUs, present in nearly all other languages, and syntactically natural in Go.

While I appreciate the value of avoiding implicit int/bool conversions, forbidding explicit ones seems slightly obtuse, and as a result of its omission, one must write four extra lines of code:

 var i int
 if b { 
     i = 1
 }
 ... i ...

which the gc compiler does not optimize this into the obvious single instruction.

The reverse operation bool(i), is not essential since i != 0 has the same effect and can be used in an expression context, but could be added for symmetry.  (I have no strong opinion.)

Since we like evidence, I count 73 occurrences in $GOROOT of an 'if' statement whose body simply assigns 1 to a variable.

% grep -wr -A2 if.*{  $GOROOT/src | grep -A1 "= 1$" | grep '}$' | wc -l
73

Jan Mercl

unread,
Dec 17, 2014, 2:40:30 PM12/17/14
to adon...@google.com, golan...@googlegroups.com


On Wed, Dec 17, 2014, 20:33 null <adon...@google.com> wrote:

I propose the following modest language change for Go 1.5.  (This is issue #9367.)

An explicit conversion int(b) where b has a boolean type would become legal, and would yield 1 if b is true, 0 otherwise.  Similar conversions would be legal for all numeric types.

Rationale: currently this function cannot be computed in a single expression, despite it being often useful, hard to misuse, efficiently implemented by modern ALUs, present in nearly all other languages, and syntactically natural in Go.

While I appreciate the value of avoiding implicit int/bool conversions, forbidding explicit ones seems slightly obtuse, and as a result of its omission, one must write four extra lines of code:


IMHO this clearly belongs to compiler optimization, not into the language.

-j

Ed Pelc

unread,
Dec 17, 2014, 2:46:06 PM12/17/14
to golan...@googlegroups.com, adon...@google.com
I haven't ever needed this but I would have expected it to be there several months ago. However it goes under the lines of another thing to remember which I know go is against. However I don't think this would be that big of a deal since it's pretty intuitive and I'm sure if I ever did need it I would have tried this.

Just wondering how many times has this occured in your own packages that you work on?

Scott Pakin

unread,
Dec 17, 2014, 3:43:15 PM12/17/14
to golan...@googlegroups.com, adon...@google.com
On Wednesday, December 17, 2014 12:40:30 PM UTC-7, Jan Mercl wrote:

IMHO this clearly belongs to compiler optimization, not into the language.

Although this seems to be the dominant opinion in the discussion of issue 9367, I respectfully disagree.  While having to write out the corresponding if statement certainly isn't a showstopper, I agree with the OP that mapping false to 0 and true to 1 doesn't violate the principle of least astonishment.  In fact, I was more astonished (about a week ago, when I last attempted this) to find that the bool-to-int cast isn't supported than I would be to learn that int(false)=0 and int(true)=1.  This is not a deep language change, doesn't break any existing code, can occasionally improve readability, and doesn't seem to me to violate Go's design principles.

— Scott

Uli Kunitz

unread,
Dec 17, 2014, 5:23:45 PM12/17/14
to golan...@googlegroups.com, adon...@google.com
By the way a function toInt(b bool) int with the obvious branch is inlined by the Go compiler. One of the cases requires two jumps on AMD64. It would be interesting to know how much faster a version without jumps would be. Maybe not much due to speculative execution and branch prediction.

roger peppe

unread,
Dec 17, 2014, 6:19:15 PM12/17/14
to Alan Donovan, golang-nuts

I've wanted this a few times. SGTM.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raffaele Sena

unread,
Dec 17, 2014, 6:39:07 PM12/17/14
to roger peppe, Alan Donovan, golang-nuts
Or maybe 1 extra lines of code:

boolInt = map[bool]int{true: 1, false: 0}

and this wherever you need it:

  x := boolInt[b]

Ian Lance Taylor

unread,
Dec 17, 2014, 7:24:47 PM12/17/14
to Raffaele Sena, roger peppe, Alan Donovan, golang-nuts
On Wed, Dec 17, 2014 at 3:38 PM, Raffaele Sena <raf...@gmail.com> wrote:
>
> Or maybe 1 extra lines of code:
>
> boolInt = map[bool]int{true: 1, false: 0}
>
> and this wherever you need it:
>
> x := boolInt[b]

Good point, and of course you don't need any actual extra lines of
code. Whenever you are tempted to write i = int(b) you can just write
i = map[bool]int{true: 1, false: 0}[b]
If people start doing that the compiler could in principle recognize
it.

Ian

Luna Duclos

unread,
Dec 18, 2014, 2:57:01 AM12/18/14
to Ian Lance Taylor, Raffaele Sena, roger peppe, Alan Donovan, golang-nuts
i'd like to see this proposal appear in some form.
Though the map syntax is very awkward, obtuse and seems generally unreasonable, I'd much prefer the possibility of an explicit conversion.

Rob Pike

unread,
Dec 18, 2014, 6:34:55 AM12/18/14
to Luna Duclos, Ian Lance Taylor, Raffaele Sena, roger peppe, Alan Donovan, golang-nuts
You're all arguing that a rarely used trivial operation that can be implemented in a couple of simple lines of code needs to be part of the language. My response is to write a function for this sort of thing. The two or three times I've needed it, that's what I've done and it did not seem an onerous task.

-rob

Tim Shannon

unread,
Dec 18, 2014, 9:27:29 AM12/18/14
to golan...@googlegroups.com, adon...@google.com
I agree that this should just be optimized out at the compiler.  If not, would you also do this implicit conversion for C.Int?

In almost every cgo lib I write I have something like this at the top, and it's fine and clear to me.


//used for bools from c interfaces
var Bool = map[int]bool{
	0: false,
	1: true,
}
var Int = map[bool]C.int{
	false: C.int(0),
	true:  C.int(1),
}

roger peppe

unread,
Dec 18, 2014, 9:36:14 AM12/18/14
to Tim Shannon, golang-nuts, Alan Donovan
On 18 December 2014 at 14:27, Tim Shannon <shannon...@gmail.com> wrote:
> I agree that this should just be optimized out at the compiler. If not,
> would you also do this implicit conversion for C.Int?
>
> In almost every cgo lib I write I have something like this at the top, and
> it's fine and clear to me.
>
>
> //used for bools from c interfaces
> var Bool = map[int]bool{
> 0: false,
> 1: true,
> }
> var Int = map[bool]C.int{
> false: C.int(0),
> true: C.int(1),
> }

It might be quite hard for the compiler to optimise that,
as it will have to wait until the whole program is linked
before it can determine that those maps are not modified
AFAICS.

By my understanding of Alan's proposal it would be
OK for any int type, so C.int(true) would be fine.

Alan Donovan

unread,
Dec 18, 2014, 9:55:15 AM12/18/14
to Rob Pike, Luna Duclos, Ian Lance Taylor, Raffaele Sena, roger peppe, golang-nuts
On 18 December 2014 at 06:34, Rob Pike <r...@golang.org> wrote:
You're all arguing that a rarely used trivial operation that can be implemented in a couple of simple lines of code needs to be part of the language.
 
Well, in $GOROOT, this operation is needed more often (≥66 times) than string -> []rune conversion (29 occurrences), and it too can be implemented in a couple of simple lines of code.


Others: let's not get distracted by the question of performance.

Scott Pakin

unread,
Dec 18, 2014, 11:59:29 AM12/18/14
to golan...@googlegroups.com, luna....@palmstonegames.com, ia...@golang.org, raf...@gmail.com, rogp...@gmail.com, adon...@google.com
On Thursday, December 18, 2014 4:34:55 AM UTC-7, Rob 'Commander' Pike wrote:
You're all arguing that a rarely used trivial operation that can be implemented in a couple of simple lines of code needs to be part of the language. My response is to write a function for this sort of thing. The two or three times I've needed it, that's what I've done and it did not seem an onerous task.

Oh, so how did the Go team justify adding

for range x {
   …
}

to Go 1.4?  Well, according to the Go 1.4 Release Notes,

This situation seemed awkward, so as of Go 1.4 the variable-free form is now legal. The pattern arises rarely but the code can be cleaner when it does.

I, for one, have never needed for range x in my Go code.  Nor do I recall ever noticing anyone on golang-nuts requesting that feature or even observing that it affects dozens of lines of code in $GOROOT.  And does the new for-range semantics really represent such a clarity or performance improvement over

for _ = range x {
   …
}

(a savings of only four characters, two if you omit spaces)?

While you're correct, Rob, that casts from bool to int can be implemented in a couple of simple lines of code and that it is not an onerous task to write those, my argument—and I believe the OP's as well—is that this feature improves the consistency of the language.  Go already allows conversions between any of the floating-point and any of the integer types, and it's not like those conversions behave more obviously than {false0, true1}.  Think of choice of rounding direction or handling overflow cases; those represent more far-reaching judgment calls than how to map Booleans to integers.

My 2¢,
— Scott

Liam

unread,
Dec 18, 2014, 6:20:29 PM12/18/14
to golan...@googlegroups.com


On Thursday, December 18, 2014 8:59:29 AM UTC-8, Scott Pakin wrote:

While you're correct, Rob, that casts from bool to int can be implemented in a couple of simple lines of code and that it is not an onerous task to write those, my argument—and I believe the OP's as well—is that this feature improves the consistency of the language. 

Amen to consistency, esp given the C lineage of Go. And the proposal is far more illuminating than any of the alternatives, onerous or otherwise.

One almost-as-accessible alternative is
i := b ? 1 : 0

...a construct I miss EVERY BLESSED HOUR in which I write Go code.

i := 0; if b { i = 1 } // is a workaround, but...
i := slow(); if b { i = expensive() } // is unworkable

Glen Newton

unread,
Dec 18, 2014, 6:59:31 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
Traditionally in Unix/Linux, 0 is true and all other values are false. i.e. return codes from programs:
 - https://en.wikipedia.org/wiki/True_and_false_%28commands%29
 - http://tldp.org/LDP/abs/html/exit-status.html

So if this were to be adopted, true should convert to 0; false should convert to 1.

That said, I don't see any pressing need for this.

-Glen


On Wednesday, December 17, 2014 2:33:17 PM UTC-5, adon...@google.com wrote:

Russ Cox

unread,
Dec 18, 2014, 7:20:28 PM12/18/14
to Glen Newton, golang-nuts, Alan Donovan
On Thu, Dec 18, 2014 at 6:59 PM, Glen Newton <glen....@gmail.com> wrote:
Traditionally in Unix/Linux, 0 is true and all other values are false. i.e. return codes from programs:
 - https://en.wikipedia.org/wiki/True_and_false_%28commands%29
 - http://tldp.org/LDP/abs/html/exit-status.html

So if this were to be adopted, true should convert to 0; false should convert to 1.

+1

Russ Cox

unread,
Dec 18, 2014, 7:38:07 PM12/18/14
to Alan Donovan, golang-nuts
The language is basically done and frozen. These kinds of tiny changes seem reasonable one by one, but if you let them all in you end up with a giant mess like C++. It is especially important to reject the changes that have trivial alternatives, like writing a 4-line function, because those changes are the least necessary.

All that said, the evidence presented is much weaker than you made it out to be. I looked at the specific instances expecting to find mostly badly converted C code, and while I did find a little, mostly I found code that is doing things other than bool->int conversions. Skimming these, hardly any would be made clearer or simpler by the ability to write int(condition). (What the compiler does and does not do today is irrelevant.)

We did consider this long ago, and there was no compelling reason to do it. There still isn't.

Russ

src/cmd/go/build.go:742
par := buildP
if buildN {
par = 1
}
src/cmd/gofmt/gofmt.go:361
}
if indent == 0 && hasSpace {
indent = 1
}
src/cmd/link/macho_test.go:219
magic := uint32(0xFEEDFACE)
if arch.CPU&macho64Bit != 0 {
magic |= 1
}
src/cmd/link/macho_test.go:374
}
if msect.Offset == 0 {
flags = 1
}
src/cmd/yacc/yacc.go:2166
nolook = 0
if tystate[i] != MUSTLOOKAHEAD {
nolook = 1
}
src/cmd/yacc/yacc.go:3038
a[i] = y
if y != x {
sub = 1
}
src/compress/bzip2/bzip2.go:383
// This is either the RUNA or RUNB symbol.
if repeat == 0 {
repeat_power = 1
}
src/compress/bzip2/huffman.go:43
bit, ok := br.TryReadBit()
if !ok && br.ReadBit() {
bit = 1
}
src/compress/flate/huffman_bit_writer.go:350
var flag int32
if isEof {
flag = 1
}
src/crypto/elliptic/p224.go:84
p224FromBig(&y1, bigY1)
if bigX1.Sign() != 0 || bigY1.Sign() != 0 {
z1[0] = 1
}
src/crypto/elliptic/p224.go:89
p224FromBig(&y2, bigY2)
if bigX2.Sign() != 0 || bigY2.Sign() != 0 {
z2[0] = 1
}
src/crypto/x509/pkcs1.go:92
version := 0
if len(key.Primes) > 2 {
version = 1
}
src/encoding/json/scanner_test.go:293
}
if f < 1 {
f = 1
}
src/encoding/json/scanner_test.go:308
}
if n > 0 && f == 0 {
f = 1
}
src/go/doc/reader.go:473
if s, ok := spec.(*ast.ImportSpec); ok {
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
r.imports[import_] = 1
}
src/go/format/format.go:240
}
if indent == 0 && hasSpace {
indent = 1
}
src/go/printer/nodes.go:590
depth--
if depth < 1 {
depth = 1
}
src/go/printer/printer.go:438
// if the previous comment was a line comment
if n == 0 && prev != nil && prev.Text[1] == '/' {
n = 1
}
src/go/printer/printer.go:1068
// Set p.indent to 1 so we don't get indent "underflow".
if _, ok := n.(*ast.LabeledStmt); ok {
p.indent = 1
}
src/go/printer/printer.go:1080
for _, s := range n {
if _, ok := s.(*ast.LabeledStmt); ok {
p.indent = 1
}
src/go/scanner/scanner_test.go:652
cnt := 0
if err != "" {
cnt = 1
}
src/image/jpeg/huffman.go:209
}
if d.bits.a&d.bits.m != 0 {
code |= 1
}
src/math/big/arith.go:39
z0 = x + yc
if z0 < x || yc < y {
z1 = 1
}
src/math/big/arith.go:49
z0 = x - yc
if z0 > x || yc < y {
z1 = 1
}
src/math/big/int.go:983
b := intGobVersion << 1              // make space for sign bit
if x.neg {
b |= 1
}
src/math/big/int_test.go:974
nreps := 20
if testing.Short() {
nreps = 1
}
src/math/big/rat.go:680
b := ratGobVersion << 1 // make space for sign bit
if x.a.neg {
b |= 1
}
src/math/sqrt.go:116
ix |= 1 << shift
if exp&1 == 1 { // odd exp, double x to make it even
ix <<= 1
}
src/net/dnsconfig_unix.go:70
n, _, _ := dtoi(s, 6)
if n < 1 {
n = 1
}
src/net/dnsconfig_unix.go:76
n, _, _ := dtoi(s, 8)
if n < 1 {
n = 1
}
src/net/dnsconfig_unix.go:82
n, _, _ := dtoi(s, 9)
if n < 1 {
n = 1
}
src/net/http/serve_test.go:1228
ms, _ := strconv.Atoi(r.URL.Path[1:])
if ms == 0 {
ms = 1
}
src/net/http/transport_test.go:1144
nRuns := 5
if testing.Short() {
nRuns = 1
}
src/net/http/transport_test.go:1212
nRuns := 5
if testing.Short() {
nRuns = 1
}
src/net/tcp_test.go:70
msgs = b.N / conns
if msgs == 0 {
msgs = 1
}
src/runtime/cgocallback.go:25
func _cgo_allocate_internal(len uintptr) unsafe.Pointer {
if len == 0 {
len = 1
}
src/runtime/gengoos.go:57
value := 0
if goos == target {
value = 1
}
src/runtime/gengoos.go:75
value := 0
if goarch == target {
value = 1
}
src/runtime/mprof.go:284
r = int64(float64(rate) * float64(tickspersecond()) / (1000 * 1000 * 1000))
if r == 0 {
r = 1
}
src/runtime/mprof.go:293
func blockevent(cycles int64, skip int) {
if cycles <= 0 {
cycles = 1
}
src/runtime/netpoll.go:413
n := pollBlockSize / pdSize
if n == 0 {
n = 1
}
src/runtime/os1_dragonfly.go:44
timeout = timediv(ns, 1000, nil)
if timeout == 0 {
timeout = 1
}
src/runtime/os1_linux.go:89
}
if n == 0 {
n = 1
}
src/runtime/os1_plan9.go:55
close(fd)
if ncpu == 0 {
ncpu = 1
}
src/runtime/os1_plan9.go:113
ms := int32(µs / 1000)
if ms == 0 {
ms = 1
}
src/runtime/os1_plan9.go:215
ms := timediv(ns, 1000000, nil)
if ms == 0 {
ms = 1
}
src/runtime/os1_windows.go:223
ns = int64(timediv(ns, 1000000, nil))
if ns == 0 {
ns = 1
}
src/runtime/os1_windows.go:527
ms = 1000 / hz
if ms == 0 {
ms = 1
}
src/runtime/panic.go:491
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
src/runtime/panic.go:504
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
src/runtime/parfor.go:104
extra := uint32(0)
if !idle {
extra = 1
}
src/runtime/pprof/pprof_test.go:233
count := 1000000
if testing.Short() {
tries = 1
}
src/runtime/proc1.go:2189
// But at least the current goroutine is running.
if n < 1 {
n = 1
}
src/runtime/race/testdata/atomic_test.go:22
x1 = 1
if atomic.AddInt64(&s, 1) == 2 {
x2 = 1
}
src/runtime/race/testdata/atomic_test.go:29
x2 = 1
if atomic.AddInt64(&s, 1) == 2 {
x1 = 1
}
src/runtime/race/testdata/atomic_test.go:44
x1 = 1
if atomic.AddInt64(&s, 1) == 1 {
x2 = 1
}
src/runtime/race/testdata/atomic_test.go:51
x2 = 1
if atomic.AddInt64(&s, 1) == 1 {
x1 = 1
}
src/runtime/race/testdata/atomic_test.go:66
x1 = 1
if atomic.AddInt32(&s, 1) == 2 {
x2 = 1
}
src/runtime/race/testdata/atomic_test.go:73
x2 = 1
if atomic.AddInt32(&s, 1) == 2 {
x1 = 1
}
src/runtime/race/testdata/atomic_test.go:157
x1 = 1
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
x2 = 1
}
src/runtime/race/testdata/atomic_test.go:164
x2 = 1
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
x1 = 1
}
src/runtime/select.go:159
// skip the default case
if n > 0 && lockorder[0] == nil {
r = 1
}
src/runtime/sqrt.go:120
ix |= 1 << shift
if exp&1 == 1 { // odd exp, double x to make it even
ix <<= 1
}
src/strconv/extfloat.go:296
f.Multiply(powersOfTen[i])
if errors > 0 {
errors += 1
}
src/strconv/ftoa.go:136
case 'g', 'G':
if prec == 0 {
prec = 1
}
src/strconv/ftoa.go:182
case 'g', 'G':
if prec == 0 {
prec = 1
}
src/strings/replace_test.go:81
n := i + 1 - 'a'
if n < 1 {
n = 1
}
src/syscall/srpc_nacl.go:575
tag = 'b'
if x {
val1 = 1
}
src/testing/benchmark.go:398
}
if grain < 1 {
grain = 1
}
src/testing/cover.go:109
}
if total == 0 {
total = 1
}
src/time/time_test.go:256
}
if d <= 0 {
d = 1
}
src/time/zoneinfo_plan9.go:84
zi := 0
if i%2 == 0 {
zi = 1
}


simon place

unread,
Dec 18, 2014, 7:41:32 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
i'd say no, based on them just being different things, although historically common its not ideal to represent logic with numbers, so having it non-trivial might make someone ask themselves "should i be using something that represents a logic state as a counter value?".

also because of this the conversion will be a matter of convention not definition, the 'extra' code makes it clear which way is being used.

and if it comes from outside, seems to me that you could often just continue to use the number, never converting.

Alan Donovan

unread,
Dec 18, 2014, 7:43:56 PM12/18/14
to Russ Cox, Glen Newton, golang-nuts
The Unix exit code zero may mean success, but that's not relevant here.

The Go spec says: "A boolean type represents the set of Boolean truth values denoted by the predeclared constants true and false."  And George Boole makes it abundantly clear in Chapter III of Laws of Thought (1854) that 0 represents the falsehood of a proposition, and nearly every work on logic and the computer representation of logic values has followed that convention.

But since it's clear that further discussion of this feature is pointless, I give up.

Rob Pike

unread,
Dec 18, 2014, 7:48:15 PM12/18/14
to Glen Newton, golan...@googlegroups.com, Alan Donovan
So if this were to be adopted, true should convert to 0; false should convert to 1

+false 

Scott Pakin

unread,
Dec 18, 2014, 7:59:26 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
On Thursday, December 18, 2014 5:38:07 PM UTC-7, Russ Cox wrote:
The language is basically done and frozen. These kinds of tiny changes seem reasonable one by one, but if you let them all in you end up with a giant mess like C++. It is especially important to reject the changes that have trivial alternatives, like writing a 4-line function, because those changes are the least necessary.

In that case, could you please clarify why for range x was added to Go 1.4 so those of us in the peanut gallery can get a better idea of the types of language improvements that are meaningful to suggest versus those that will be immediately rejected?  As I see it, for range x has a trivial alternative and is totally unnecessary.  So what am I missing here?

Thanks,
— Scott

Scott Pakin

unread,
Dec 18, 2014, 8:11:23 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
On Thursday, December 18, 2014 4:59:31 PM UTC-7, Glen Newton wrote:
Traditionally in Unix/Linux, 0 is true and all other values are false. i.e. return codes from programs:
 - https://en.wikipedia.org/wiki/True_and_false_%28commands%29
 - http://tldp.org/LDP/abs/html/exit-status.html

So if this were to be adopted, true should convert to 0; false should convert to 1.

Gack!  You can't imagine how maddening I find that whenever I do shell programming.  I assume Unix went that route because it lacks multiple return values, like what Go conventionally uses to report errors.  Because there's a single way a program can succeed but various ways it can fail (program doesn't like its input, program segfaults, etc.), defining any nonzero value (not just 1) to mean failure is a cheap hack to piggyback additional information onto what ought to be a pure Boolean value.

— Scott

Ian Lance Taylor

unread,
Dec 18, 2014, 8:16:05 PM12/18/14
to Scott Pakin, golang-nuts, Alan Donovan
On Thu, Dec 18, 2014 at 4:59 PM, Scott Pakin <scot...@pakin.org> wrote:
>
> In that case, could you please clarify why for range x was added to Go 1.4
> so those of us in the peanut gallery can get a better idea of the types of
> language improvements that are meaningful to suggest versus those that will
> be immediately rejected? As I see it, for range x has a trivial alternative
> and is totally unnecessary. So what am I missing here?

The discussion about "for range x" is in http://golang.org/issue/6102
and https://codereview.appspot.com/104680043 . You can see that we
didn't rush into it; it was almost a year from proposal to
implementation. An argument in favor of the change was that we can
normally omit the trailing unnecessary variables in language defined
assignment statements: we don't require "for a, _ = range x".
Similarly, we don't require "switch _ = x.(type)".

Ian

Scott Pakin

unread,
Dec 18, 2014, 8:25:12 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
On Thursday, December 18, 2014 5:41:32 PM UTC-7, simon place wrote:
i'd say no, based on them just being different things, although historically common its not ideal to represent logic with numbers

It's a valid objection that logic values and numbers are different.  However, mapping the two does have a lot of historical precedent (George Boole, as mentioned earlier, plus the Iverson bracket appearing in a programming language in the early 1960s) so it's not that outrageous to provide such a conversion.  What appeals to me is the consistency of being able to map any built-in number-like thing to any other built-in number-like thing.
 
also because of this the conversion will be a matter of convention not definition, the 'extra' code makes it clear which way is being used.

So why not replace int(myFloat) with a function call that lets you specify the rounding mode?  It's a matter of convention, not definition, that int(myFloat) rounds towards zero, and an explicit function call would make it clear which rounding mode is being used.

— Scott

Tad Vizbaras

unread,
Dec 18, 2014, 8:32:58 PM12/18/14
to golan...@googlegroups.com
I am glad Go team added for range {} . I am already using it.

By the way I was missing a function for rounding floats into ints for about a year. 
I simply implemented it and was done.

I think freezing language for the time being is a good idea. People find ways to 
use the language. New idioms are born without bloating the core.

Scott Pakin

unread,
Dec 18, 2014, 8:34:21 PM12/18/14
to golan...@googlegroups.com, scot...@pakin.org, adon...@google.com
On Thursday, December 18, 2014 6:16:05 PM UTC-7, Ian Lance Taylor wrote:
The discussion about "for range x" is in http://golang.org/issue/6102
and https://codereview.appspot.com/104680043 .  You can see that we
didn't rush into it; it was almost a year from proposal to
implementation.  An argument in favor of the change was that we can
normally omit the trailing unnecessary variables in language defined
assignment statements: we don't require "for a, _ = range x".
Similarly, we don't require "switch _ = x.(type)".  

Thanks for the clarification.  The first link definitely has a Through the Looking Glass feel to it, with a core Go developer (Rob) arguing for a minor language change and various outsiders criticizing it for being an unnecessary and useless language change.

— Scott

Rob Pike

unread,
Dec 18, 2014, 8:39:40 PM12/18/14
to Scott Pakin, golan...@googlegroups.com, Alan Donovan
In the honesty department, although I do like the regularization that "for range" brought in, and am glad that it is there, even this simple change has caused a fair number of problems due to version skew.

Even the simplest changes can be disruptive.

-rob


--

anl...@gmail.com

unread,
Dec 18, 2014, 8:47:24 PM12/18/14
to golan...@googlegroups.com, adon...@google.com
the solution: use the built in == operator.

 if foo == 1 {..} else {..}

anl...@gmail.com

unread,
Dec 18, 2014, 8:51:00 PM12/18/14
to golan...@googlegroups.com, adon...@google.com, anl...@gmail.com
sorry you want the other way? use ++ operator.

if bar {n++}

simon place

unread,
Dec 18, 2014, 9:16:49 PM12/18/14
to golan...@googlegroups.com, adon...@google.com


On Friday, 19 December 2014 01:25:12 UTC, Scott Pakin wrote:
On Thursday, December 18, 2014 5:41:32 PM UTC-7, simon place wrote:
i'd say no, based on them just being different things, although historically common its not ideal to represent logic with numbers

It's a valid objection that logic values and numbers are different.  However, mapping the two does have a lot of historical precedent (George Boole, as mentioned earlier, plus the Iverson bracket appearing in a programming language in the early 1960s) so it's not that outrageous to provide such a conversion. 
 
sure, but i just think we should evolve away from it,  making it obvious, not hiding it, is a start.  For me what currently exists is better.

 
also because of this the conversion will be a matter of convention not definition, the 'extra' code makes it clear which way is being used.

So why not replace int(myFloat) with a function call that lets you specify the rounding mode?  It's a matter of convention, not definition, that int(myFloat) rounds towards zero, and an explicit function call would make it clear which rounding mode is being used.

isn't that stuff defined in an IEEE standard that Go says it adheres to, precisely so it is defined.

Alan Donovan

unread,
Dec 19, 2014, 9:20:24 AM12/19/14
to Russ Cox, golang-nuts
On 18 December 2014 at 19:37, Russ Cox <r...@golang.org> wrote:
All that said, the evidence presented is much weaker than you made it out to be.

Fair point; I should have looked harder.

John Waycott

unread,
Dec 19, 2014, 9:31:28 AM12/19/14
to golan...@googlegroups.com, luna....@palmstonegames.com, ia...@golang.org, raf...@gmail.com, rogp...@gmail.com, adon...@google.com


On Thursday, December 18, 2014 9:59:29 AM UTC-7, Scott Pakin wrote:
While you're correct, Rob, that casts from bool to int can be implemented in a couple of simple lines of code and that it is not an onerous task to write those, my argument—and I believe the OP's as well—is that this feature improves the consistency of the language.  Go already allows conversions between any of the floating-point and any of the integer types, and it's not like those conversions behave more obviously than {false0, true1}.  Think of choice of rounding direction or handling overflow cases; those represent more far-reaching judgment calls than how to map Booleans to integers.


I'd argue the opposite. int/float conversions are very different from int/bool or int/string conversions. Floats and ints are both numbers, but bool is not a number; it is it's own type. Go doesn't implement string(i) either, not in the general sense of string(123) == "123". It does the sensible thing of only converting characters, a conversion that can be precisely defined.

Scott Pakin

unread,
Dec 19, 2014, 12:15:50 PM12/19/14
to golan...@googlegroups.com, adon...@google.com
On Thursday, December 18, 2014 7:16:49 PM UTC-7, simon place wrote:
isn't that stuff defined in an IEEE standard that Go says it adheres to, precisely so it is defined.
  1. The Go specification could just as easily have stated it adheres to a definition that mathematicians proposed 160 years ago and that has been the standard definition in programming languages for 50 years.  It doesn't, but that doesn't mean it couldn't.
  2. Just like there's not a single definition for bool-to-int conversions, there's not a single definition of float64-to-int conversions.  The IEEE floating-point standard in fact defines five rounding modes.  Go could have chosen any of those five and still been compliant with IEEE 754.  It went with "round towards zero", which is the prevalent rounding mode in today's programming languages, just like {false0, true1} is prevalent in today's programming languages.
— Scott

Kevin Gillette

unread,
Dec 19, 2014, 1:48:18 PM12/19/14
to golan...@googlegroups.com, adon...@google.com
While using Go, there have been two situations in which I've been confronted with C-style boolean interpretation of integers (0 is false, anything else is true) -- in one of those cases, my application approach was misguided anyway, and in the other case, a straightforward conditional was the "natural" choice -- I didn't think about or wish for a type conversion.

simon place

unread,
Dec 19, 2014, 2:50:46 PM12/19/14
to golan...@googlegroups.com, adon...@google.com
On Friday, 19 December 2014 17:15:50 UTC, Scott Pakin wrote:
  1. The Go specification could just as easily have stated it adheres to a definition that mathematicians proposed 160 years ago
no, maths doesn't say what 2 etc is.


            and that has been the standard definition in programming languages for 50 years.

no, common in programming languages is  true-1, because -1 in binary two's comp is all 1's.

  1. Just like there's not a single definition for bool-to-int conversions, there's not a single definition of float64-to-int conversions.  The IEEE floating-point standard in fact defines five rounding modes.  Go could have chosen any of those five and still been compliant with IEEE 754.  It went with "round towards zero", which is the prevalent rounding mode in today's programming languages, just like {false0, true1} is prevalent in today's programming languages.
— Scott

it may not be simple, (because IMHO it is "unnatural"), but it is defined.



chris dollin

unread,
Dec 19, 2014, 3:03:36 PM12/19/14
to simon place, golang-nuts, adon...@google.com
On 19 December 2014 at 19:50, simon place <simon...@googlemail.com> wrote:

> no, common in programming languages is true→-1, because -1 in binary two's
> comp is all 1's.

I'd hesitate to call it "common" given that neither of C (and C++ I
suspect) and Java have true be -1.

(And in (older now?) C there's the difference between what's
used as a boolean in control contexts (if, while, for), which is
"0 is false, everything else isn't" and as the result of the built-in
relational operators ("return 0 for false and 1 for true").)

It seems to me that in Go the compelling argument would be that
the zero value of bool should convert to the zero value of (some)
int(eger type) and vice versa, which fixes false = 0, and since
some integer types don't have negative values one would have to
play song-and-dance about specifying what true would convert to
in what circumstances and so picking 1 would be less hassle.

Chris

--
Chris "allusive" Dollin

simon place

unread,
Dec 19, 2014, 6:36:09 PM12/19/14
to golan...@googlegroups.com, simon...@googlemail.com, adon...@google.com
AFAIK  Java's like Go, and its not built-in.

but i remember why it was -1, it makes bit twiddling on signed number much more sensible/possible.

(with unsigned numbers often not well supported because of hardware issues.)

i used this a lot on windows for interfacing to drivers.

atd...@gmail.com

unread,
Dec 19, 2014, 7:03:15 PM12/19/14
to golan...@googlegroups.com, adon...@google.com
I had no opinion on this, but given how the discussion has headed, I find that keeping boolean types and numeric types segregated at the spec. level is clearer. :-)
Reply all
Reply to author
Forward
0 new messages