# Re: [go-nuts] Function signature irritation in Golang

722 views

### Jan Mercl

Jan 20, 2013, 12:09:32 PM1/20/13
On Sun, Jan 20, 2013 at 7:42 AM, Prasanth Guruprasad <nag...@gmail.com> wrote:
> How have you solved this problem?

http://godoc.org/github.com/cznic/mathutil#Max

-j

### steve wang

Jan 20, 2013, 1:02:02 PM1/20/13
Well, I would like to make one on my own:
func Max(a, b int) int {
if a > b {return a}
return b
}

On Sunday, January 20, 2013 2:42:24 PM UTC+8, Prasanth Guruprasad wrote:
Consider this simple piece of go code.

func SomeFunc(val1, val2 int) int {
max := math.Max(val1, val2)
return max
}

Hold it, I know what you're thinking "But math.Max() can take in only float64s". Right. So, in order to make the code compile, I have to do this :

func SomeFunc(val1, val2 int) int {
max := int(math.Max(float64(val1), float64(val2)))
return max
}

It looks very verbose for doing something very simple. Is there any way I can add a function to the math package that takes in ints abd return an int (Like extension methods in C#).
The function signature I add would be like this :

func Max(val1, val2 int) int

### Kevin Gillette

Jan 20, 2013, 1:06:24 PM1/20/13
math.Max is not intended for int types. I would write my own, which is exactly:

func Max(a, b int) int {
if a < b {
return a
}
return b
}

You should almost always attempt to avoid trying to use functions that take floats's in order to process ints, since the float and int number types have very different properties.

On 64 bit platforms, a go 'int' is 64 bits, and a float64 can only represent integral values between 0 and +- 2^53. For each order of magnitude further from zero, the precision halves, so 2^60 and 2^60+127 may be indistinguishable, which is a major problem (you will likely convert the output to an int that doesn't match one of the inputs you gave to math.Max.

In go, just because you can convert between numeric types to use a function in the stdlib doesn't mean it's a good idea -- the stdlib may provide functions for non-trivial cases (look at the source for math.Max, which handles NaN and +-Inf), yet not provide implementations for the type you care about -- when this happens, consider very carefully whether it's reasonable to convert (such as with float32), or if it's such a simple func to write that you're expected to write it yourself.

### Kevin Gillette

Jan 20, 2013, 1:11:42 PM1/20/13
I meant:

func Max(a, b int) int {

if a >= b {
return a
}
return b
}

### Job van der Zwan

Jan 20, 2013, 1:43:40 PM1/20/13
Or, alternatively:

... depending on what coding style you prefer.

PS: bit of thread hijacking: damn Jan, I should spend an evening to catch up with your package! Maybe build those i8/i16/u8/u16/bytes versions after all... And since I adapted stuff from your code, I have a link to your package on the page and in the code, is that sufficient credit? I'm not sure what the etiquette is regarding this. Similarly, any thoughts on what licence I should put in there? I'm fine with a "do whatever you want with it" kind of thing.

### Job van der Zwan

Jan 20, 2013, 1:52:38 PM1/20/13
On Sunday, 20 January 2013 19:43:40 UTC+1, Job van der Zwan wrote:
Similarly, any thoughts on what licence I should put in there? I'm fine with a "do whatever you want with it" kind of thing.

I am an idiot who forgot what he did half a year ago - the package has a New BSD license. Apologies for the noise.

### Jan Mercl

Jan 20, 2013, 1:54:21 PM1/20/13
to Job van der Zwan, golang-nuts, Prasanth Guruprasad
On Sun, Jan 20, 2013 at 7:43 PM, Job van der Zwan
<j.l.van...@gmail.com> wrote:

> PS: bit of thread hijacking: damn Jan, I should spend an evening to catch up
> with your package! Maybe build those i8/i16/u8/u16/bytes versions after
> all...

;-)

> package on the page and in the code, is that sufficient credit? I'm not sure
> what the etiquette is regarding this.

That's AFAIK mostly just up to you if you do or do not mention others.

> Similarly, any thoughts on what
> licence I should put in there? I'm fine with a "do whatever you want with
> it" kind of thing.

This one is different. The code (in mathutil) is covered by a company
BSD license, not mine, so you should comply to what's written in that
license. That's not what I demand, that's what the license says you
must do to use the code.

-j

### minux

Jan 20, 2013, 11:58:23 PM1/20/13
to Jan Mercl, Prasanth Guruprasad, golang-nuts
Just a quick thought about the mathutil package.

Max and MaxInt is slightly confusing, as I'd expect MaxInt to take
two ints and return the bigger one.

Also, I'd expect the maximum integer to be a const instead of a function.
(hint: it is even possible to make the constant an untyped integer)

### Jan Mercl

Jan 21, 2013, 8:52:40 AM1/21/13
On Mon, Jan 21, 2013 at 5:58 AM, minux <minu...@gmail.com> wrote:
> Just a quick thought about the mathutil package.
>
> Max and MaxInt is slightly confusing, as I'd expect MaxInt to take
> two ints and return the bigger one.

I understand. mathutil.MaxInt, MinInt, ... is in the lines of
math.MaxInt32, MinInt32 and friends. Had no better naming scheme idea
at that time, neither I do now (how to properly solve the naming
"overlap").

> Also, I'd expect the maximum integer to be a const instead of a function.
> (hint: it is even possible to make the constant an untyped integer)

Agreed completely. IMO this was worth breaking the API:
http://github.com/cznic/mathutil/commit/1a8500b7323982bdf2ff136169275e69587c2c46

-j

PS: Sorry to anyone who might get some existing code broken by this
change. Not that I am aware of anyone using this package.

### Kevin Gillette

Jan 21, 2013, 12:11:57 PM1/21/13
Historical hindsight: MaxInt for the function, IntMax for the constant.

Jan 21, 2013, 10:45:58 PM1/21/13
Well, I would like to make one on my own:
func Max(a, b int) int {
if a > b {return a}
return b
}
This might be possible for this case, since the implementation logic is very simple.If the function is more complex, I might not want to do that - for code duplication and maintainance purposes. However, I might implement a function that takes in int args and performs the casting internally and makes the call to the math.Max() that takes in float args. Not to mention typecasting the result to int again.

### Kevin Gillette

Jan 22, 2013, 12:36:25 AM1/22/13
Again, you _really_ do _not_ want to use float64's as intermediates for integer operations. Not all potential Go int values are representable in a float64. For your own sake, don't... This also applies to using a double in C as an intermediate for long long values. Not good. Genericism isn't worth the very real opportunity you'd be introducing for subtle, dangerous bugs to break your program at runtime. Also keep in mind that if you're strongly interested in Go, accept that Go does favor not-invented-here (code duplication) much more than most of the popular "modern" languages do -- when it's this simple, and when the alternatives (math.Max for int handling) are that problematic, either write your own, or find a library that at least does what you need with in the same kind of type (converting one int to another, not int <-> float)

If you limit yourself to int32, uint32, or smaller, when float64 is used as an intermediate, then you're sometimes technically safe (depending on _what_ you're doing), but as a note of caution, doing that may be considered quite ridiculous (I personally would avoid using any library that does that, and recommend that others avoid it, since it's a warning sign for even deeper problems).

If you want a set of somewhat generic functions for all int values, use int64 and uint64 as the types, and write it yourself:

func MaxInt(a, b int64) int64 {
if a >= b {
return a
}
return b
}

func MaxUint(a, b uint64) uint64 {
if a >= b {
return a
}
return b
}

This way, you can always safely do something like:

var x, y int16 = 12, 9, 0
z := int16(MaxInt(x, y))

var p, q byte = 3, 8, 0
r := byte(MaxUint(p, q))

### Kevin Gillette

Jan 22, 2013, 12:53:07 AM1/22/13
Just to demonstrate how dangerous, in actual real life (yes, really), the idea is of using float64 intermediates for int operations: http://play.golang.org/p/RrZXeSgmft

Run it, and you'll see that "the max of x and y is neither x nor y, but less than both!" I'm sure that's exactly what you expected, right? Any proximal values in the range [2^54,2^64) will have this pathological behavior. Any proximal values in the range [2^53, 2^54) will at least have the result equal to either value, but with this implementation, it will still act like a minimizing function instead of having the maximizing behavior that one would hope. And 2^53 isn't even that large compared to the maximum int64 value (it's a least a difference of ten orders of magnitude).

### bryanturley

Jan 22, 2013, 1:55:53 AM1/22/13

On Monday, January 21, 2013 11:53:07 PM UTC-6, Kevin Gillette wrote:
Just to demonstrate how dangerous, in actual real life (yes, really), the idea is of using float64 intermediates for int operations: http://play.golang.org/p/RrZXeSgmft

Run it, and you'll see that "the max of x and y is neither x nor y, but less than both!" I'm sure that's exactly what you expected, right? Any proximal values in the range [2^54,2^64) will have this pathological behavior. Any proximal values in the range [2^53, 2^54) will at least have the result equal to either value, but with this implementation, it will still act like a minimizing function instead of having the maximizing behavior that one would hope. And 2^53 isn't even that large compared to the maximum int64 value (it's a least a difference of ten orders of magnitude).

Generally speaking it is only 3-4 orders of magnitude larger....

2**53
9007199254740992
18446744073709551616
2**64

I guess in base 2 it is 10 orders though, but that is pretty uncommon.

### Kevin Gillette

Jan 22, 2013, 2:50:23 AM1/22/13
I was thinking in terms of powers of two, but you'd be right, it's not nearly 10 when you consider magnitude relative to 2^53.  The scale was somewhat irrelevant next to the main point, though ;-P

### Mateusz Czapliński

Jan 23, 2013, 5:36:01 AM1/23/13
On Sunday, January 20, 2013 7:06:24 PM UTC+1, Kevin Gillette wrote:
math.Max is not intended for int types. I would write my own, which is exactly:

func Max(a, b int) int {
if a < b {
return a
}
return b
}

I think that's exactly the reason why some of us would prefer to have this in library. So that we'd not have to bother about 6 more lines we have to write and where we can make some damn stupid errors.

/M.