inconsistent min max signatures with variadics

257 views
Skip to first unread message

Benoît Marguerie

unread,
Jan 20, 2025, 10:00:04 AM1/20/25
to golang-nuts
Hi,

Even if the spec precises :
slice arguments are not permitted

I wonder if there's any real technical reason to prohibit the use of the slice variadics during min/max buildin functions, when it's allowed with append (which creates inconsistent behavior between buildin functions)?

Example:
```
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7}
padding := []int{8, 9}

slice = append(slice, padding...)
forbidden := max(5, slice...) //Doesn't build due to this line....

fmt.Println(forbidden)
}
```

Stephen Illingworth

unread,
Jan 20, 2025, 10:14:54 AM1/20/25
to golang-nuts
What value do you think should be returned for "max(5, slice)" ?

Benoît Marguerie

unread,
Jan 20, 2025, 10:21:10 AM1/20/25
to golang-nuts
Hi Stephen,
max(5,slice) has no sense by itself, I'm agree with you.

But the issue is about max(5,slice...) which should accept the "unpacking" operation like the append function. I know there's a buildin type "Type" for append while the min/max functions use the Generic with the interface cmp.Ordered but from an "usage point of view", it doesn't seem "understandable".

Stephen Illingworth

unread,
Jan 20, 2025, 10:33:16 AM1/20/25
to golang-nuts
Oh I see what you mean now.

Should max(slice...) be allowed (ie. just the slice and no unary value) ? It would behave like slices.Max() I think. Maybe there is deep reason for not allowing but it would be nice to have for sure.

Benoît Marguerie

unread,
Jan 20, 2025, 10:50:56 AM1/20/25
to golang-nuts
If I remember well, max(slice...) existed but not anymore: it disappeared with Go 1.21 I think. The idea was to force one element at least -> why not, that's the "min/max business" I may say. 
But the "unpacking" operator is a builtin and should be allowed for all func with a variadic input param. For min/max, it's not the case.... And as you said "there is deep reason" which I don't really understand (not really technically but much more as global understood of the ... operator mecanism -> why unpacking couldn't be used with generic ?!)

Ian Lance Taylor

unread,
Jan 20, 2025, 1:43:17 PM1/20/25
to Benoît Marguerie, golang-nuts
On Mon, Jan 20, 2025 at 7:00 AM Benoît Marguerie <bmar...@gmail.com> wrote:
>
> Even if the spec precises :
> > slice arguments are not permitted
>
> I wonder if there's any real technical reason to prohibit the use of the slice variadics during min/max buildin functions, when it's allowed with append (which creates inconsistent behavior between buildin functions)?

Calling append with an empty slice is well defined. Calling min or max
with an empty slice is not.

Ian

Dan Kortschak

unread,
Jan 20, 2025, 2:35:40 PM1/20/25
to golan...@googlegroups.com
On Mon, 2025-01-20 at 10:42 -0800, Ian Lance Taylor wrote:
> Calling append with an empty slice is well defined. Calling min or
> max with an empty slice is not.

If the only input to min/max were the slice, this would be true, but I
do not think it is in the case that is implemented in the standard
library where the signatures are fn(T, ...T), e.g. s := []int{};
max(42, s...) is 42.

Benoît Marguerie

unread,
Jan 20, 2025, 3:28:34 PM1/20/25
to golang-nuts
Thanks Ian and Dan for your returns. 
I'm pretty agree with Dan considering that if append can understand that an empty slice provides nothing to add, I think min/max should be able to understand that an empty slice provides nothing to compare to the first param. Moreover, today, it is possible to write "max(5)" exactly like with an empty slice...
What is the more confusing for me is that it seems that the "unpacking" operator is not evaluated before func call, but during func call (and blocked). I suppose that's linked to an optimization/performence reason but it's working fine with every func which take a variadiac param  (like append but every custom func too): except min/max...

Ian Lance Taylor

unread,
Jan 20, 2025, 5:36:32 PM1/20/25
to Dan Kortschak, golan...@googlegroups.com
I would say that this is because the language doesn't provide a way to
write what the spec says. The spec says "The built-in functions min
and max compute the smallest—or largest, respectively—value of a fixed
number of arguments of ordered types. There must be at least one
argument." The language doesn't support writing that function
declaration. That is, the declaration is not really fn(T, ...T). It's
fn(T, T, T, ...) where there must be at least one value.

We could change the definition of min/max to permit writing max(42,
s...). But today the spec does not permit that. And to me the change
doesn't seem worth making. We already have slices.Min and slices.Max.

Ian

Dan Kortschak

unread,
Jan 20, 2025, 5:54:43 PM1/20/25
to golan...@googlegroups.com
Agreed. My understanding was that this was because the min and max
functions get written as an unrolled set of cmp instructions, rather
than as a loop, and while this was not included in the spec for obvious
reasons, it is why the spec was written that way.

Dan

Benoît Marguerie

unread,
Jan 21, 2025, 4:56:18 AM1/21/25
to golang-nuts
ok. Thanks Ian and Dan for your explanations even if I understand that the reason is just an implementation reason which introduces a bias on unpacking operator (seperation of concern would have be great here I suppose but maybe less efficient).
Reply all
Reply to author
Forward
0 new messages