Integer Division and Modulo as a single operator?

6,871 views
Skip to first unread message

ajventi

unread,
Mar 23, 2012, 5:19:21 PM3/23/12
to golang-nuts
I'm a long time lover of Lisp, and recently began using go. So far I'm
quite happy with the language, and have found I can develop rapidly
without most of the headaches other languages tend to bring about.

When I saw that go returned multiple values (a feature of Lisp) I was
looking to see if there is a go equivalent of Lisp's TRUNCATE which
returns the integer quotient and modulo as a single operation.:

> (TRUNCATE 11 2)
5
1

I always thought it silly that more languages have not had a similar
function as this is a single operation on the processor.

I would assume most well designed compilers would pick up on some code
like:

q := n / d
r := n % d

and optimize it by only using a single operation.

However
q,r := n %/ d
seems like much sexier code, and go has the ability to make it happen.

Hotei

unread,
Mar 23, 2012, 6:00:48 PM3/23/12
to golan...@googlegroups.com
This is easy enough to do with a ModDiv() function that returns two results.  I doubt the end result of a discussion of operator overloading has changed much so I'd research that thread before proceeding on your quest.

Michael Jones

unread,
Mar 23, 2012, 5:49:17 PM3/23/12
to ajventi, golang-nuts
I like the idea too. Though it tends to get rejected each time it is
brought up. There are four in total:

sum, overflow := a + b
difference, underflow := a - b
product, high_product := a * b
quotient, remainder := a / b

Oh, how beautiful these would be! It would be poetry...

Your program must rely,
on these four arithmetic friends;
Each, two answers supply,
on which all correctness depends.

--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1
650-335-5765

Kyle Lemons

unread,
Mar 23, 2012, 6:55:46 PM3/23/12
to Michael Jones, ajventi, golang-nuts
I know the Go team has been systematically killing off multi-assignment magic, but this is probably the one "add a feature" proposal that I support.  I see the arguments about it not holding its own weight, but for a systems language, they seem like really important things to have.

Liigo Zhuang

unread,
Mar 23, 2012, 8:55:42 PM3/23/12
to ajventi, golang-nuts

LGFM

Mikael Gustavsson

unread,
Mar 23, 2012, 9:51:33 PM3/23/12
to golan...@googlegroups.com, ajventi
On Saturday, March 24, 2012 5:49:17 AM UTC+8, Michael Jones wrote:
I like the idea too. Though it tends to get rejected each time it is
brought up. There are four in total:

sum, overflow := a + b
difference, underflow := a - b
product, high_product := a * b
quotient, remainder := a / b

That is seriously cool!
It's also in line with the Go philosophy of explicit error handling.

But I doubt it's going to happen due to the overloading of return types of the basic operators. 

Hotei

unread,
Mar 23, 2012, 10:13:24 PM3/23/12
to golan...@googlegroups.com, ajventi
The problem is that _most_ of the time I don't want/need to write:

sum, _ := a + b  

and how to handle more complex formulae like:

result, err  := math.Sqrt((a-b) * (a+b))

is err an overflow, underflow, high_product or a sqrt(neg) error?

Isn't that confusion a logical result of this proposal?   My 2c .

Matt Kane's Brain

unread,
Mar 23, 2012, 10:18:50 PM3/23/12
to Hotei, golan...@googlegroups.com, ajventi
On Fri, Mar 23, 2012 at 22:13, Hotei <hote...@gmail.com> wrote:
> The problem is that _most_ of the time I don't want/need to write:
>
> sum, _ := a + b

This could be handled like a type assertion, or a map lookup:

v, ok := a[x] // ok is false if a has no element with key x
r, ok := i.(io.Reader) // ok is false if i doesn't implement io.Reader

Without the multi-assignment, v is assigned a zero value (as it is
above) and the assignment to r causes a panic.

--
matt kane's brain
http://hydrogenproject.com

Steven Blenkinsop

unread,
Mar 23, 2012, 10:46:47 PM3/23/12
to Matt Kane's Brain, Hotei, golan...@googlegroups.com, ajventi
On 2012-03-23, at 10:18 PM, "Matt Kane's Brain" <mkb-...@hydrogenproject.com> wrote:

> On Fri, Mar 23, 2012 at 22:13, Hotei <hote...@gmail.com> wrote:
>> The problem is that _most_ of the time I don't want/need to write:
>>
>> sum, _ := a + b
>
> This could be handled like a type assertion, or a map lookup:

-1. I'm already not a fan of the existing cases where this happens (particularly with channels).

John Asmuth

unread,
Mar 23, 2012, 11:10:17 PM3/23/12
to golan...@googlegroups.com, ajventi
Built-ins don't suffer from the need to explicitly ignore values. They can simply rely on context.

Hotei

unread,
Mar 23, 2012, 11:21:14 PM3/23/12
to golan...@googlegroups.com, ajventi
John,
"They can simply rely on context" - is that saying they can deduce my intent?   I find that hard to imagine even in the trivial case listed below.

ajventi

unread,
Mar 23, 2012, 11:35:20 PM3/23/12
to golang-nuts
Interestingly enough I hadn't realized that map references in go are
basically identical to the way Lisp hash works.

I guess I should point out I'm not a fan of _. In Lisp you can simply
ignore multiple values, Lisp only carps if you ask it to. The first
value returned is the important one where the rest are things that
make your life easier and your code more robust.

So go gives us _ but why not just do away with that altogether?
Generally go returns the most important value first except for a
for .. := range on an array(slice) where the value tends to be more
important than the index. Providing both is simply syntatic sugar.

Now as much as Mikael's plan is nice, but as Hotei pointed out, just
think how it would be trying to teach people a language where "x = 1 +
b" is an error and explaining that they need to write "x, _ = 1 + b"
we could just rename the language to "stop" at that point.

Now that we're talking about the only place a comma is valuable I may
have to start a thread on my hatred of commas.

Steven Blenkinsop

unread,
Mar 24, 2012, 1:20:19 AM3/24/12
to Hotei, golan...@googlegroups.com, ajventi
On Fri, Mar 23, 2012 at 11:21 PM, Hotei <hote...@gmail.com> wrote:
John,
"They can simply rely on context" - is that saying they can deduce my intent?   I find that hard to imagine even in the trivial case listed below.

As in the expression defaults to single-valued form unless used in a special multiple-assignment was used, like for type assertions, channel operations, and map accesses. So, `a / b` is a single valued expression as now, but in the special form `x, r := a/b", you get the two value result. I personally think Go needs less of these special multiple assignment forms, not more, though the horse is already out of the barn.

unread,
Mar 24, 2012, 3:10:33 AM3/24/12
to golan...@googlegroups.com, ajventi
On Friday, March 23, 2012 10:49:17 PM UTC+1, Michael Jones wrote:
I like the idea too. Though it tends to get rejected each time it is
brought up. There are four in total:

sum, overflow := a + b
difference, underflow := a - b
product, high_product := a * b
quotient, remainder := a / b

I also like the idea. I would change multiplication into:

  product, high_product, overflow := a * b

Ignoring the high_product:

  product, _, overflow := a * b

unread,
Mar 24, 2012, 3:22:57 AM3/24/12
to golan...@googlegroups.com, ajventi
On Saturday, March 24, 2012 3:13:24 AM UTC+1, Hotei wrote:
The problem is that _most_ of the time I don't want/need to write:

sum, _ := a + b  

a+b would return an overflow only if the programmer explicitly requests it. There are only two cases:

  sum, overflow := a+b
  sum, overflow = a+b

In all other usage cases, a+b returns a single value.
 

and how to handle more complex formulae like:

result, err  := math.Sqrt((a-b) * (a+b))

1. The expression "sum,overflow := a+b" is expected to work with integer types only. Floating point addition already supports overflow.

2. There is no need for math.Sqrt as such to return an error. Sqrt never fails.

Lars Pensjö

unread,
Mar 24, 2012, 6:33:50 AM3/24/12
to golan...@googlegroups.com, ajventi
Wouldn't this proposal add another case of inconsistency?

With the proposal, there would be some cases where you can ignore multiple return values, and some where you can't. Every additional case of inconsistency makes the language harder to learn and understand. One thing I really like in Go is the low number of exceptions you need to know or understand.

John Asmuth

unread,
Mar 24, 2012, 8:42:23 AM3/24/12
to golan...@googlegroups.com, ajventi
Maybe they can't infer your intent, but they can see what the context indicates.

// single-valued
func Foo(x float64) { ... }
Foo(x/y)

// single-valued
z := x/y

// single-valued
if x/y == 7 { ... }

// two-valued
w, x := y/z

// two-valued
func Foo(w, x float64) { ... }
Foo(y/z)

This is no different from the contextual awareness of map indexing.

// single-valued
aVal := aMap[aKey]

// two-valued
aVal, ok : = aMap[aKey]

Liigo Zhuang

unread,
Mar 24, 2012, 9:20:13 AM3/24/12
to John Asmuth, ajventi, golan...@googlegroups.com

+1

Steven Blenkinsop

unread,
Mar 24, 2012, 2:06:33 PM3/24/12
to John Asmuth, golan...@googlegroups.com, ajventi
On Sat, Mar 24, 2012 at 8:42 AM, John Asmuth <jas...@gmail.com> wrote:
// two-valued
func Foo(w, x float64) { ... }
Foo(y/z)

This one wouldn't work, if you use map indexing and other comma-ok cases as a template. In all cases the expression is single valued, except when it's very obvious because of the multiple assignment form that you want the second value. Otherwise, it isn't clear to a reader that Foo1(y/z) is unary, but Foo2(y/z) is binary. And if someone misses an argument by accident, it may get automatically filled in by the compiler and you would have a very difficult to detect bug.

Martin Geisler

unread,
Mar 24, 2012, 3:16:31 PM3/24/12
to Lars Pensjö, golan...@googlegroups.com, ajventi
Lars Pensjö <lars....@gmail.com> writes:

> Wouldn't this proposal add another case of inconsistency?

Yes, it would -- don't do it!

> With the proposal, there would be some cases where you can ignore
> multiple return values, and some where you can't. Every additional
> case of inconsistency makes the language harder to learn and
> understand. One thing I really like in Go is the low number of
> exceptions you need to know or understand.

I began looking at Go a couple of weeks ago. I'm sorry to say it, but
the first impression was that Go is a terribly *inconsistent* language.

For a newly designed language, there is too many "magic" things that the
built-in operators and functions can do that user-defined functions
cannot do. I'm thinking of

value, ok := some_map[key] vs value := some_map[key]

It might have looked cool and you even gave it a name (the "comma ok"
idiom). But I cannot do the same for a normal function:

foo, err := os.Open("foo.txt")
foo := os.Open("foo.txt")

The magic "make" and "new" functions also have special powers that we
cannot use. First, they take a type as argument -- that's already iffy.
Second, they take a variable number of arguments without being variadic.

The "range" operator/function/clause is also weird in the same way that
the "comma ok" idiom is weird: you can ignore a second return value
without using _. You cannot ignore the first, so you have to write

for _, value := range some_map { ... }

Would it not have been much more consistent to always require two
variables? Even better: give maps keys() and values() methods (that
return slices) that you can iterate over. Abstract that into an
interface and you can suddenly iterate over user-defined types.

Strings also feels inconsistent to me. They are very much like immutable
byte slices, except that you iterate over UTF-8 decoded code points?!
That's very nice and practical, but now you've implicitly defined the
format of strings to be UTF-8. Not quite, though, since the range
operator will produce 0xFFFD if it encounters invalid UTF-8 bytes. How
can such a string even be produced?


This is just a brain-dump after playing with Go for some weeks. As a new
user it feels inconsistent. Not broken and not something you cannot get
used to, but the inconsistencies stand out.

--
Martin Geisler

Mercurial links: http://mercurial.ch/

John Asmuth

unread,
Mar 24, 2012, 3:39:17 PM3/24/12
to golan...@googlegroups.com, Lars Pensjö, ajventi
On Saturday, March 24, 2012 3:16:31 PM UTC-4, Martin Geisler wrote:

This is just a brain-dump after playing with Go for some weeks. As a new
user it feels inconsistent. Not broken and not something you cannot get
used to, but the inconsistencies stand out.

I believe it is not true that a language needs to be inconsistent. "Fairness" adds no benefit to anyone who isn't trying to tinker with and extend the language via userspace. It doesn't actually make it easier to write real code.

Opinion, of course. But one coming from someone with a couple years of practical Go experience.

Go is certainly not a very extensible language, unless you delve into the compiler internals. But that's not the point. The point is to more quickly write more efficient code that is less buggy.

andrey mirtchovski

unread,
Mar 24, 2012, 5:00:54 PM3/24/12
to Martin Geisler, golan...@googlegroups.com
> As a new user it feels inconsistent. Not broken and not something you cannot get
> used to, but the inconsistencies stand out.

One of the nice things Go has got going for it, in my opinion: the
inconsistencies are in-your-face when you start using it, but they're
a relatively small number (you've pretty much enumerated them all,
leaving one or two gotchas having to do with closures or goroutines
that catch newcomers). It's a set that one can definitely keep track
of in their head and quickly get accustomed to if they write Go code
primarily.

There are no other major things that will surprise you when you delve
in deeper, the way lazy evaluation might in Haskell, or generators
might in Python. It's all out up front.

Michael Jones

unread,
Mar 24, 2012, 11:32:07 AM3/24/12
to Liigo Zhuang, John Asmuth, ajventi, golan...@googlegroups.com
BACKGROUND

To clarify, I should not have used the terms overflow and underflow. I was thinking "expose the hardware truth" and that's what the corresponding values are usually called in CPUs, but in fact, at the logical, mathematics level there is no such thing as over or under-flow. so 'carry' and 'borrow' are better words.

Adding two n-digit signed integers in any base may produce an n+1 digit result. When this happens in real life (5+5 = 10) we consider it as ordinary, when it happens in code (5+5 = 0) we also take no special action and the program continues on with "0" and ignores the "overflow == carry of 1 == lost sum equal to 10" situation. NOTE: This truncation of the high digit happens in [asymptotically] 50% of same-sign additions. (2147450880 of 4294967296 16-bit additions, or about 49.999237060546875%) The CPU does not ignore these cases but high level languages do. It has always been a concern of mine that otherwise careful language designers are blind to this--as a child (10 years old/4th grade, writing in assembler) I learned to treat overflow carefully.  

Now we have Go, with clean multiple value assignment. It could be used to simplify the natural fact that fixed-precision basic integer arithmetic operations always produce two results, just as it could be used in the normal usage case where the programmer and program desire to hide from this fact.

sum := a + b
sum, carry := a + b // ",carry" optional, and ONLY in simple addend+augend expression
sum, carry := a SpecialTwoValuedAdditionSymbol b
sum, carry := SpecialTwoValueAdditionFunction(a, b)

difference := a - b
difference, borrow := a - b // ",borrow" optional, and ONLY in simple minuend-subtrahend expression
difference, borrow := a SpecialTwoValuedSubtractionSymbol b
difference, borrow := SpecialTwoValuedSubtractionFunction(a, b)

The carry and borrow will be zero if a and b are of different signs and +/- 1 in the overflow case. The sign of the borrow could arguably be positive or negative, I have an opinion but it does not matter what convention is chosen (are you borrowing 1 x -(2^n) or are you borrowing -1 x 2^n.)

For multiplication the situation is the same in presentation but extremely different in distribution. Multiplying two n-digit signed integers may produce numbers with up to 2n digits in length. When this happens in real life (10*10 = 100 or 99*99 = 9801) we see no adventure, just as in Go code where 10*10 = 0 and 99*99 = 1 (substituting binary of course for this decimal example) causes no panic and is business as usual. Where there is a difference in character from addition is in how many of the possible multiplications overflow. First, differing signs do not save you as they do with addition. Let's consider an n-digit, base 10 multiplication table (starting at 0) and count how many of the entries are >= 10^n and thus would truncate. The answer is:

0..9 x 0..9: 58 of 100 = 58%
0..99 x 0.99: 9328 of 10000 = 93.28%
0..999 x 0.999: 990948 of 1000000 = 99.0948%

For 16-bit binary multiplication the total is 4294099268 of 4294967296, which means 99.97978964820504% of possible 16-bit x 16-bit multiplications produce a non-zero upper 16-bits of product. (That is just 2 in 10,000 that don't overflow, which may seem shocking but is consistent with the asymptotic sense--half for addition and all for multiplication.) This is so significant that every CPU has been a champ and handling this. Despite the commonality of the upper-half "overflow" product and perfect handling of it in CPUs, it is perfectly ignored in high-level languages. (Solutions are to use bigger word sizes or floating point.) However, in Go with the natural means for two results from one expression, we can have:

product := a * b
product, upper := a * b // ",upper" optional, and ONLY in simple multiplier*multiplicand expression
product, upper := a SpecialTwoValuedMultiplicationSymbol b
product, upper := SpecialTwoValueMultiplicationFunction(a, b)

Division is the remaining basic integer operation, and we have the mathematical quotient and remainder via two single purpose operators:

quotient := a / b  // truncating integer division
remainder := a % b // modulus operation == remainder of division operation

This is much better than the above three cases because we are already offered access to the secondary result by a secondary function. It is the same concept as:

sum := a + b
carry := a CarryFromAdd b

or 

product := a * b
upper := a UpperProduct b

and in all three cases we would like to mandate that the compiler produce the two results from the single machine operation that is implied. Cleaner, though, and more clearly one logical operation would be a single and dual-result operations:

quotient := a / b
quotient, remainder := a / b

and maybe

_,remainder = a / b  // just an idea -- not arguing for this.

How often is the remainder non-zero? Precisely when numerator and denominator are relatively prime. There is depth in that question but a simple tabulation suffices:

1..9 / 1..9: 58 of 81 relatively prime = 71.60493827160493%
1..99 / 1..99: 9328 of 9801 relatively prime = 95.17396184062851%
1.999 / 1.999: 990948 of 998001 relatively prime = 99.29328728127527%

For 16-bit unsigned integers, the answer is that 4294099268 of 4294836225 (99.98284085908304%) divisions have a non-zero remainder so we should be grateful that we at least have the modulus operator even if it presently takes two lines of code and replication of the numerator and denominator expressions to compute a simple quotient and remainder. (Note that the non-zero remainder case in division is more frequent than a non-zero upper product in multiplication--loosely an "almost always" situation.)

I would be proud if Go were the first systems language to make the reality of addition, subtraction, multiplication, and division become first-class elements of the language.

Michael

Liigo Zhuang

unread,
Mar 24, 2012, 9:33:46 PM3/24/12
to Michael Jones, John Asmuth, golan...@googlegroups.com, ajventi

+1

Thanks.

Liigo Zhuang

unread,
Mar 24, 2012, 9:41:43 PM3/24/12
to Martin Geisler, ajventi, golan...@googlegroups.com, Lars Pensjö

+1 Yes, you're right.

ajventi

unread,
Mar 25, 2012, 8:23:35 AM3/25/12
to golang-nuts
Well since I guess this was a proposal of sorts let me modify and
clarify somewhat.

Quick summary:

1. Add a binary operator or function that returns both the quotient
and modulo.
I propose /% as an operator, otherwise math.Divmod(a, b int) (q, m
int)

2. Is there any reason to require we assign multiple values? Why not
simply allow us to ignore a second result rather than force us to use
`_'

I hadn't realized that a map reference didn't require the second
value to be assigned. It's a strange inconsistency, but I'd rather
that become the standard rather than the underscore. If I want to
ignore something, and the language allows me to ignore it, why should
I have to tell the compiler "I'm ignoring that"

I should also point out that in LISP the TRUNCATE function is what
returns Quotient and Modulo. The / function in Lisp is a generic, if
you call it with two integers it returns a fraction.

Liigo Zhuang

unread,
Mar 25, 2012, 8:43:04 AM3/25/12
to ajventi, golang-nuts

+1

Rémy Oudompheng

unread,
Mar 25, 2012, 8:49:53 AM3/25/12
to ajventi, golang-nuts
Le 25 mars 2012 14:23, ajventi <ajv...@gmail.com> a écrit :
> Well since I guess this was a proposal of sorts let me modify and
> clarify somewhat.
>
> Quick summary:
>
> 1. Add a binary operator or function that returns both the quotient
> and modulo.
>    I propose /% as an operator, otherwise math.Divmod(a, b int) (q, m
> int)

In what kind of programs do you expect to use that?

> 2. Is there any reason to require we assign multiple values? Why not
> simply allow us to ignore a second result rather than force us to use
> `_'

I'm not sure what is the use for a new magic operator if you are going
to ignore return values.

Rémy.

si guy

unread,
Mar 25, 2012, 11:18:12 AM3/25/12
to golan...@googlegroups.com
I use this kind of operation all of the time for getting I,j,k,....,m coordinates in an n-dimensional array that is stored in a one-dimensional array (or slice).

ziutek

unread,
Mar 25, 2012, 4:32:26 PM3/25/12
to golang-nuts
> 1. Add a binary operator or function that returns both the quotient
> and modulo.
>     I propose /% as an operator, otherwise math.Divmod(a, b int) (q, m
> int)
>
> 2. Is there any reason to require we assign multiple values? Why not
> simply allow us to ignore a second result rather than force us to use
> `_'


What about this:

c, d := a + b, a /% b
c, d, r := a + b, a /% b

It seems to be a little confusing to me.

I think that it will be sufficient if the compiler specification will
guarantee that:

d, r := a / b, a % b

will be optimized on platforms that perform such operation using one
machine instruction.

Rémy Oudompheng

unread,
Mar 25, 2012, 4:34:25 PM3/25/12
to ajventi, golang-nuts
Le 25 mars 2012 21:43, ajventi <ajv...@gmail.com> a écrit :
>> In what kind of programs do you expect to use that?
>
> Really? Have you ever wondered what sort of magic it takes to convert
> an integer into a string?

Yes, I have. And there's already a package doing that. It can be
optimized and expanded. My question is more about real world programs
that would have a clear benefit out of "q, r := a /% b" vs. "q, r :=
a/b, a%b".

I just don't see an added value for a new operator from the discussion
(as opposed to the detection of overflow, which I think would be
valuable). I think that /% is not really readable, and I can't think
of a symbol that looks readable. Also I don't think performance
matters as a criterion to decide what a language should look like,
except maybe, for the overall design, but not at that level of detail.

And about performance, I'd like to know if performance would be
improved by adding that operator and mapping it to the corresponding
instruction. Do you have benchmarks?

Rémy.

Job van der Zwan

unread,
Mar 26, 2012, 2:45:45 AM3/26/12
to golan...@googlegroups.com, ajventi
On Sunday, 25 March 2012 22:34:25 UTC+2, Rémy Oudompheng wrote:

And about performance, I'd like to know if performance would be
improved by adding that operator and mapping it to the corresponding
instruction. Do you have benchmarks?

I'm not sure how you would benchmark that, short of

- writing a simple program where this proposal would simplify the code
- disassembling it
- rewriting the relevant parts of it in assembler by hand

Liigo Zhuang

unread,
Mar 26, 2012, 4:46:20 AM3/26/12
to ziutek, golang-nuts

+1
The easiest way to do it currently.

Martin Geisler

unread,
Mar 26, 2012, 8:22:01 AM3/26/12
to andrey mirtchovski, golan...@googlegroups.com
andrey mirtchovski <mirtc...@gmail.com> writes:

>> As a new user it feels inconsistent. Not broken and not something you
>> cannot get used to, but the inconsistencies stand out.
>
> One of the nice things Go has got going for it, in my opinion: the
> inconsistencies are in-your-face when you start using it, but they're
> a relatively small number (you've pretty much enumerated them all,
> leaving one or two gotchas having to do with closures or goroutines
> that catch newcomers). It's a set that one can definitely keep track
> of in their head and quickly get accustomed to if they write Go code
> primarily.

Thanks, that is a another good point that I hadn't considered :)

--
Martin Geisler

aragost Trifork
Professional Mercurial support
http://www.aragost.com/mercurial/

Martin Geisler

unread,
Mar 26, 2012, 8:20:33 AM3/26/12
to John Asmuth, golan...@googlegroups.com, Lars Pensjö, ajventi
John Asmuth <jas...@gmail.com> writes:

> On Saturday, March 24, 2012 3:16:31 PM UTC-4, Martin Geisler wrote:
>>
>> This is just a brain-dump after playing with Go for some weeks. As a
>> new user it feels inconsistent. Not broken and not something you
>> cannot get used to, but the inconsistencies stand out.
>
> I believe it is not true that a language needs to be inconsistent.
> "Fairness" adds no benefit to anyone who isn't trying to tinker with
> and extend the language via userspace. It doesn't actually make it
> easier to write real code.

That's an interesting point and I admit that I haven't thought of it
like this before.

For me, the inconsistencies makes it (ever so slightly) harder to get a
firm mental model for the language. I'm used to Python, and there's it's
no mystery what 'range' is -- it's a function like any other. So

for i in range(10): ...

is easy: range(10) returns a generator that I can iterate over. An
iterator is itself just an object that implements a ceratain interface.
Since it's just a normal object, I can trivially do

seq = range(10)
for i in seq: ...

and get the same result. With Go I cannot do

seq := range some_array

and then iterate over the array later -- range is "magic" somehow and I
don't know what it "is". (Of course I know what it is, it's a syntax
that can be used in a for-loop to iterate over certain types. But I
somehow feel that this explanation is more opaque than in Python.)

Jesse McNelis

unread,
Mar 26, 2012, 12:23:41 PM3/26/12
to Martin Geisler, golan...@googlegroups.com
On Mon, Mar 26, 2012 at 11:20 PM, Martin Geisler <m...@aragost.com> wrote:
> I'm used to Python, and there's it's
> no mystery what 'range' is -- it's a function like any other. So
>
>  for i in range(10): ...

But what is 'in'?
'in' is usually a boolean operator
eg.

seq = range(10)
i = 2
a = i in seq
# a is True

But in a for loop 'in' magically also assigns to i and has a
completely different behaviour.

i = 2
for i in [1,2,3,4,5,6]:
print(i)
i = 20

for some reason my Boolean expression is acting in a very strange way
and the loop isn't break when i is no longer within the list.

--
=====================
http://jessta.id.au

andrey mirtchovski

unread,
Mar 26, 2012, 12:40:19 PM3/26/12
to Martin Geisler, golan...@googlegroups.com
> For me, the inconsistencies makes it (ever so slightly) harder to get a
> firm mental model for the language. I'm used to Python, and there's it's
> no mystery what 'range' is -- it's a function like any other. So

if "range" is so simple then why does python need "xrange"? what's the
difference between them? why use one instead of the other? is range
even thread-safe?

Andrew Gallant

unread,
Mar 26, 2012, 1:39:38 PM3/26/12
to golang-nuts
> if "range" is so simple then why does python need "xrange"? what's the
> difference between them? why use one instead of the other? is range
> even thread-safe?

"xrange" is the generator version of "range." It's useful particularly
when the collection you're iterating over is large in size. (xrange
makes sure only one element is in memory whereas range forces all
elements to occupy memory.)

At least, that's my understanding of the difference.

- Andrew

Kyle Lemons

unread,
Mar 26, 2012, 2:19:05 PM3/26/12
to Michael Jones, Liigo Zhuang, John Asmuth, ajventi, golan...@googlegroups.com
I've been thinking about this.  As much as

quo, rem := dd / div

looks sexy, the following does not:

quo, rem := a*b / c*d
(yes, it is deliberately incorrect)

For both clarity and distinction, I would slightly prefer the addition of builtin add(), mul(), sub(), and div() functions with either fixed or variadic returns.

quo, rem := div(a*b, c*d)

John Asmuth

unread,
Mar 26, 2012, 2:28:03 PM3/26/12
to Kyle Lemons, Michael Jones, Liigo Zhuang, ajventi, golan...@googlegroups.com
On Mon, Mar 26, 2012 at 2:19 PM, Kyle Lemons <kev...@google.com> wrote:
quo, rem := a*b / c*d
(yes, it is deliberately incorrect)

Huh - I thought have thought that times would come before divide. I guess that only serves your point.
 
For both clarity and distinction, I would slightly prefer the addition of builtin add(), mul(), sub(), and div() functions with either fixed or variadic returns.

Maybe in a package, rather than built-in?

Michael Jones

unread,
Mar 26, 2012, 2:30:04 PM3/26/12
to John Asmuth, Kyle Lemons, Liigo Zhuang, ajventi, golan...@googlegroups.com
The dual value form only makes sense for a single operation. I have suggested it exclusively that way during the past year or more.

Martin Geisler

unread,
Mar 26, 2012, 2:32:24 PM3/26/12
to Jesse McNelis, golan...@googlegroups.com
Jesse McNelis <jes...@jessta.id.au> writes:

> On Mon, Mar 26, 2012 at 11:20 PM, Martin Geisler <m...@aragost.com> wrote:
>> I'm used to Python, and there's it's no mystery what 'range' is --
>> it's a function like any other. So
>>
>>  for i in range(10): ...
>
> But what is 'in'? 'in' is usually a boolean operator eg.
>
> seq = range(10)
> i = 2
> a = i in seq
> # a is True
>
> But in a for loop 'in' magically also assigns to i and has a
> completely different behaviour.

Hmm... :-) I think the crucial point was the ":=" part for me. In Go you
write

for v := range someArray { ... }

and in Python you write

for v in someSeq: ...

There's no assignment (=) in Python and "for <vars> in <something>" is
the syntax. The equivalent in Go is "for <vars> := range <something>".
The use of ":=" somehow looked weird to me.

> i = 2
> for i in [1,2,3,4,5,6]:
> print(i)
> i = 20
>
> for some reason my Boolean expression is acting in a very strange way
> and the loop isn't break when i is no longer within the list.

Hmm... :-) I see "for ... in" as a single construct and never thought
twice about that "in" is also a binary operator.

I get your point and I'll get used to the syntax. Thanks for the
discussion!

Michael Shields

unread,
Mar 26, 2012, 2:36:01 PM3/26/12
to andrey mirtchovski, Martin Geisler, golan...@googlegroups.com


On Mon, Mar 26, 2012 at 9:40 AM, andrey mirtchovski <mirtc...@gmail.com> wrote:
if "range" is so simple then why does python need "xrange"?

It doesn't, and Python 3 has only the generator range.

Kyle Lemons

unread,
Mar 26, 2012, 2:37:41 PM3/26/12
to Michael Jones, John Asmuth, Liigo Zhuang, ajventi, golan...@googlegroups.com
On Mon, Mar 26, 2012 at 11:30 AM, Michael Jones <m...@google.com> wrote:
The dual value form only makes sense for a single operation. I have suggested it exclusively that way during the past year or more.

Ah.  I don't think I'd seen you mention that it wouldn't be available if there were more than a single operation, but that makes a lot of sense too.

Michael Jones

unread,
Mar 26, 2012, 2:56:18 PM3/26/12
to Kyle Lemons, John Asmuth, Liigo Zhuang, ajventi, golan...@googlegroups.com
Sorry. It certainly causes the two obvious objections:

1. What a special case! The "comma,overflow" is optional only in simple, single expressions.
2. What a rare case! How many people ever check for overflow!

Both are true. I admit that. However, how else would one code these cases portably? The answer is like this (for division, if there were no mod):

//want q,r := n/d, where 'n' and 'd' are arbitrary expressions.

t1 := n
t2 := d
// ...check for zero divisor here, then...
q := n/d
r := n - q*d

As it happens, this division case is by far the fastest to compute compared to the others, and even here, compilers are not good. If you look at Go now, you'll see that...

q := n/10
r := n%10

is 15% or so slower than

q := n/10
r := n - (q << 3 + q)

even though q and r are right there in CPU registers waiting to be use directly. It is much worse for multiplicative overflow, where the only real solutions involve breaking each factor into a high and low part (in BASE bitsize/2) and evaluating (a*BASE+b)*(c*BASE+d) in stages, again, even though the high product is right there in CPU registers waiting to be used with a single register move or test.

Maybe a reasonable compromise would be special functions (one of my example approaches):

s,o := SpecialAdd(a, b)
d,u := SpecialSubtract(a, b)
l,h := SpecialMultiply(a, b)
q,r := SpecialDivide(a, b)

Where the compiler agrees that whatever names are chosen are reserved and handled inline.

Michael Jones

unread,
Mar 26, 2012, 2:57:19 PM3/26/12
to Kyle Lemons, John Asmuth, Liigo Zhuang, ajventi, golan...@googlegroups.com
Oops, 

r := n - (q << 3 + q << 1)


On Mon, Mar 26, 2012 at 11:56 AM, Michael Jones <m...@google.com> wrote:
r := n - (q << 3 + q)



Steven Blenkinsop

unread,
Mar 26, 2012, 3:54:16 PM3/26/12
to Martin Geisler, Jesse McNelis, golan...@googlegroups.com
On Mar 26, 2012, at 2:32 PM, Martin Geisler <m...@aragost.com> wrote:

Hmm... :-) I think the crucial point was the ":=" part for me. In Go you
write

 for v := range someArray { ... }

and in Python you write

 for v in someSeq: ...

There's no assignment (=) in Python and "for <vars> in <something>" is
the syntax. The equivalent in Go is "for <vars> := range <something>".
The use of ":=" somehow looked weird to me.

I guess the way to see it is that the `range` is loop syntax that controls the loop and retrieves different values from the following expression on each iteration, like `in`, except that you then have to assign these values to variables rather than having it automatically bound to the identifier to the left of the keyword. Having the `:=` or `=` in there gives you control over declaration, which is a useful feature.

This and `switch x := v.(type)` are examples of where there's a bit of a "huh, how does that syntax compose, exactly?" moment when learning it. It isn't the obvious design. However, it is syntactically unambiguous, and in an odd kind of way suggests its intent. So it works, even if it seems odd.

Paul Borman

unread,
Mar 26, 2012, 3:59:25 PM3/26/12
to Michael Jones, Kyle Lemons, John Asmuth, Liigo Zhuang, ajventi, golan...@googlegroups.com
I overall agree with Michael.  I have found that expression is also much more difficult than concept, and expression is what the lexer and parser are all about.

I frequently need both the quotient and remainder and I am always annoyed that I have to repeat the operation twice.

q, r := n/d, n%d

is probably worse than

q := n/d
r := n%d

and

q, r := n/d

really feels the most natural to me as a human.

I also have, not quite as frequently, had to check for carry, but it is always a special case:

// assume a is positive
na := n + a
if na < n {
    // we overflowed
}

Note the assume comment.  For multiplication I simply give up and if possible use double the bit width (and hope that 64 bits is always enough).

To me it seems Michael is often working with real mathematics, unlike OS guys like me who think floating point is something for users :-)  But even so, I see good reason to not throw away so much of what the CPU does for us.  It is not as useless as nearly all modern languages make it appear.

For me the WHAT is "return us more complete results from CPU arithmetic operations" and I will leave the HOW up to the Go designers.  The have done a good job thus far!

    -Paul

Kyle Lemons

unread,
Mar 26, 2012, 4:09:06 PM3/26/12
to Paul Borman, Michael Jones, John Asmuth, Liigo Zhuang, ajventi, golan...@googlegroups.com
On Mon, Mar 26, 2012 at 12:59 PM, Paul Borman <bor...@google.com> wrote:
I overall agree with Michael.  I have found that expression is also much more difficult than concept, and expression is what the lexer and parser are all about.

I frequently need both the quotient and remainder and I am always annoyed that I have to repeat the operation twice.

q, r := n/d, n%d

is probably worse than

q := n/d
r := n%d

and

q, r := n/d

really feels the most natural to me as a human.

I also have, not quite as frequently, had to check for carry, but it is always a special case:

// assume a is positive
na := n + a
if na < n {
    // we overflowed
}

Note the assume comment.  For multiplication I simply give up and if possible use double the bit width (and hope that 64 bits is always enough).

To me it seems Michael is often working with real mathematics, unlike OS guys like me who think floating point is something for users :-)  But even so, I see good reason to not throw away so much of what the CPU does for us.  It is not as useless as nearly all modern languages make it appear.

For me the WHAT is "return us more complete results from CPU arithmetic operations" and I will leave the HOW up to the Go designers.  The have done a good job thus far!

+1

I suspect that, if given the ability, many more programmers will find that they've always wanted the feature but never known to ask for it.

Martin Geisler

unread,
Mar 26, 2012, 2:34:12 PM3/26/12
to andrey mirtchovski, golan...@googlegroups.com
andrey mirtchovski <mirtc...@gmail.com> writes:

It doesn't really "need" it -- xrange is just there in case you're happy
with an generator instead of a list. As you probably know, Python 3 only
has xrange (and it has been renamed to range).

kortschak

unread,
Mar 26, 2012, 7:07:24 PM3/26/12
to golan...@googlegroups.com
I fall soundly in that category with the project I am working on now.

Job van der Zwan

unread,
Mar 27, 2012, 5:41:49 AM3/27/12
to golan...@googlegroups.com, Michael Jones, Kyle Lemons, John Asmuth, Liigo Zhuang, ajventi
On Monday, 26 March 2012 21:59:25 UTC+2, Paul Borman wrote:
For me the WHAT is "return us more complete results from CPU arithmetic operations" and I will leave the HOW up to the Go designers.  The have done a good job thus far!
+1 

Sindre Myren

unread,
Mar 27, 2012, 6:12:45 PM3/27/12
to golang-nuts
Den 22:49 23. mars 2012 skrev Michael Jones <m...@google.com> følgende:
I like the idea too. Though it tends to get rejected each time it is
brought up. There are four in total:

sum, overflow := a + b
difference, underflow := a - b
product, high_product := a * b
quotient, remainder := a / b


A seperate operator set should be used if this where to be implemented. For instance:

sum, overflow := a +% b
difference, underflow := a -% b
product, high_product := a *% b
quotient, remainder := a /% b

or something like that..

I really do NOT like the idea of using context based assumptions to find out whether to care about the last parameter. 

Ian Lance Taylor

unread,
Mar 27, 2012, 8:58:12 PM3/27/12
to Sindre Myren, golang-nuts
Sindre Myren <smy...@gmail.com> writes:

> A seperate operator set should be used if this where to be implemented. For
> instance:
>
> sum, overflow := a +% b
> difference, underflow := a -% b
> product, high_product := a *% b
> quotient, remainder := a /% b
>
> or something like that..

Why don't we just write some ordinary functions?

Ian

Sindre Myren

unread,
Mar 27, 2012, 9:16:09 PM3/27/12
to Ian Lance Taylor, golang-nuts
Like this?

sum, overflow := AddWithOverflow(a, b)
difference, underflow := SubtractWithUnderFlow(a , b)
product, high_product := MultiplicationWithHighProduct(a, b)
quotient, remainder := DivisionWithRemainder(a, b)

One would loose the "one operation" on the CPU point, which seamed to be of interest in this discussion, would one not? 

Ian

Personlay, I don't care to much - it would be cool to have it in the language, but the practical use is probably not that large.

Ian Lance Taylor

unread,
Mar 27, 2012, 9:23:48 PM3/27/12
to Sindre Myren, golang-nuts
Sindre Myren <smy...@gmail.com> writes:

> Den 02:58 28. mars 2012 skrev Ian Lance Taylor <ia...@google.com> følgende:
>
>> Sindre Myren <smy...@gmail.com> writes:
>>
>> > A seperate operator set should be used if this where to be implemented.
>> For
>> > instance:
>> >
>> > sum, overflow := a +% b
>> > difference, underflow := a -% b
>> > product, high_product := a *% b
>> > quotient, remainder := a /% b
>> >
>> > or something like that..
>>
>> Why don't we just write some ordinary functions?
>>
>>
> Like this?
>
> sum, overflow := AddWithOverflow(a, b)
> difference, underflow := SubtractWithUnderFlow(a , b)
> product, high_product := MultiplicationWithHighProduct(a, b)
> quotient, remainder := DivisionWithRemainder(a, b)

Yes.

> One would loose the "one operation" on the CPU point, which seamed to be of
> interest in this discussion, would one not?

We can write them in assembler.

Ian

Michael Jones

unread,
Mar 27, 2012, 11:57:10 PM3/27/12
to Ian Lance Taylor, Sindre Myren, golang-nuts
This is fine. What's unfortunate is that the desired result is already
there as the effect of a single instruction. the add and subtract
instructions _already set the overflow/underflow bit, and so on. Just
would be great to have access to the reality of the operation, and to
have it efficiently.

I yield that it will be a function, which anyone can easily write,
it's just sad that the bit/word/value to be computed after the
function call is already there in the registers!

--

Sanjay Menakuru

unread,
Mar 28, 2012, 12:45:30 AM3/28/12
to golan...@googlegroups.com, Ian Lance Taylor, Sindre Myren
I think Ian's suggestion of writing the functions in assembly would avoid having to recompute information. You can just return the second register value directly.

Sanjay

Kyle Lemons

unread,
Mar 28, 2012, 2:56:08 PM3/28/12
to Ian Lance Taylor, Sindre Myren, golang-nuts
On Tue, Mar 27, 2012 at 6:23 PM, Ian Lance Taylor <ia...@google.com> wrote:
Sindre Myren <smy...@gmail.com> writes:

> Den 02:58 28. mars 2012 skrev Ian Lance Taylor <ia...@google.com> følgende:
>
>> Sindre Myren <smy...@gmail.com> writes:
>>
>> > A seperate operator set should be used if this where to be implemented.
>> For
>> > instance:
>> >
>> > sum, overflow := a +% b
>> > difference, underflow := a -% b
>> > product, high_product := a *% b
>> > quotient, remainder := a /% b
>> >
>> > or something like that..
>>
>> Why don't we just write some ordinary functions?
>>
>>
> Like this?
>
> sum, overflow := AddWithOverflow(a, b)
> difference, underflow := SubtractWithUnderFlow(a , b)
> product, high_product := MultiplicationWithHighProduct(a, b)
> quotient, remainder := DivisionWithRemainder(a, b)

Yes.

Unfortunately, this requires one function per operation (4) per signedness (2) per bit width (4), so 32 different functions, unless I'm missing something.  Otherwise you miss out entirely on the point of the second operands.

Kyle Lemons

unread,
Mar 28, 2012, 2:57:08 PM3/28/12
to Ian Lance Taylor, Sindre Myren, golang-nuts
On Wed, Mar 28, 2012 at 11:56 AM, Kyle Lemons <kev...@google.com> wrote:
On Tue, Mar 27, 2012 at 6:23 PM, Ian Lance Taylor <ia...@google.com> wrote:
Sindre Myren <smy...@gmail.com> writes:

> Den 02:58 28. mars 2012 skrev Ian Lance Taylor <ia...@google.com> følgende:
>
>> Sindre Myren <smy...@gmail.com> writes:
>>
>> > A seperate operator set should be used if this where to be implemented.
>> For
>> > instance:
>> >
>> > sum, overflow := a +% b
>> > difference, underflow := a -% b
>> > product, high_product := a *% b
>> > quotient, remainder := a /% b
>> >
>> > or something like that..
>>
>> Why don't we just write some ordinary functions?
>>
>>
> Like this?
>
> sum, overflow := AddWithOverflow(a, b)
> difference, underflow := SubtractWithUnderFlow(a , b)
> product, high_product := MultiplicationWithHighProduct(a, b)
> quotient, remainder := DivisionWithRemainder(a, b)

Yes.

Unfortunately, this requires one function per operation (4) per signedness (2) per bit width (4), so 32 different functions, unless I'm missing something.  Otherwise you miss out entirely on the point of the second operands.

Oh, I forgot about float32 and float64 and possibly complex64 and complex128 (I'm not sure if they make sense).

Sindre Myren

unread,
Mar 28, 2012, 7:18:28 PM3/28/12
to Kyle Lemons, Ian Lance Taylor, golang-nuts
Den 20:57 28. mars 2012 skrev Kyle Lemons <kev...@google.com> følgende:
On Wed, Mar 28, 2012 at 11:56 AM, Kyle Lemons <kev...@google.com> wrote:
On Tue, Mar 27, 2012 at 6:23 PM, Ian Lance Taylor <ia...@google.com> wrote:
Sindre Myren <smy...@gmail.com> writes:

> Den 02:58 28. mars 2012 skrev Ian Lance Taylor <ia...@google.com> følgende:
>
>> Sindre Myren <smy...@gmail.com> writes:
>>
>> > A seperate operator set should be used if this where to be implemented.
>> For
>> > instance:
>> >
>> > sum, overflow := a +% b
>> > difference, underflow := a -% b
>> > product, high_product := a *% b
>> > quotient, remainder := a /% b
>> >
>> > or something like that..
>>
>> Why don't we just write some ordinary functions?
>>
>>
> Like this?
>
> sum, overflow := AddWithOverflow(a, b)
> difference, underflow := SubtractWithUnderFlow(a , b)
> product, high_product := MultiplicationWithHighProduct(a, b)
> quotient, remainder := DivisionWithRemainder(a, b)

Yes.

Unfortunately, this requires one function per operation (4) per signedness (2) per bit width (4), so 32 different functions, unless I'm missing something.  Otherwise you miss out entirely on the point of the second operands.

Oh, I forgot about float32 and float64 and possibly complex64 and complex128 (I'm not sure if they make sense).


overflow, underflow and high_product makes sense for floats as well (http://steve.hollasch.net/cgindex/coding/ieeefloat.html). The reminder probably does not.

If implemented in the asembler, I guess the functions could be written to support several int types, and I guess float as well. Though if implemented as functions, my personal belief, is that programeres would rather use the old operators, as in a programmers head, operators are faster then function calls.

Also, if I was to present to my friends the awesome and unique operator functionality of Go 1.1, the conversation would go:
Me: "Check out this awesome operators in Go 1.1! They give you both the quotient and the reminder in one operation!"
Mate: "Awesome"

If it was implemented as functions, the conversation would go like:
Me: "Look, there is a function in Go that gives you the quotient and the reminder in one operation!"
Mate: "Cool"

In other words, just a little bit less awesome.
Reply all
Reply to author
Forward
0 new messages