[generics] is generics with constrain needed ?

343 views
Skip to first unread message

Christophe Meessen

unread,
Jun 17, 2020, 4:19:49 AM6/17/20
to golang-nuts
Reading the document "Type parameters - Draft desing" of June 16, 2020 (https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md), I wonder if these two following function definitions are not equivalent and thus interchangeable

func Stringify(type T Stringer)(s []T) (ret []string)

func Stringify(s []Stringer) (ret []string)

If the constrain is an interface, wouldn’t it be the same as to use the interface as type ? How would it be different ?

David Anderson

unread,
Jun 17, 2020, 4:37:35 AM6/17/20
to Christophe Meessen, golang-nuts
The non-generic form _only_ accepts an argument of type []Stringer. If you have a []Foo, where Foo implements Stringer, that's not good enough. You will have to construct a []Stringer explicitly, and copy each Foo into that []Stringer. This is a fairly common gotcha with Go, where folks expect the implicit conversion to happen.

OTOH, the generic form will accept []Foo directly, since Foo satisfies the Stringer constraint.

- Dave

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3b758827-2d14-4769-bf94-95cf60a06b1bo%40googlegroups.com.

Miguel Angel Rivera Notararigo

unread,
Jun 17, 2020, 6:22:58 AM6/17/20
to David Anderson, Christophe Meessen, golang-nuts
Allowing this implicit conversion would be a bad thing? because it works like generics without any new syntax.

Also, I found this a little messy

type Ordered interface {
	type int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64, uintptr,
		float32, float64,
		string
}

Why not after interface?

type Ordered interface type {
	int, int8, int16, int32, int64,
	uint, uint8, uint16, uint32, uint64,
byte, rune, uintptr
, float32, float64, string, }

Sorry, I am pretty new to this, so probably the answers might be really obvious 😅

Tyler Compton

unread,
Jun 17, 2020, 1:55:23 PM6/17/20
to Miguel Angel Rivera Notararigo, David Anderson, Christophe Meessen, golang-nuts
The reason why implicit conversions from []Foo to []Stringer isn't supported is because Foo and Stringer have different in-memory representations. Converting from a slice of one to a slice of another would be more expensive than one would expect an implicit conversion to be. At least, this is my understanding as to why.

Ian Lance Taylor

unread,
Jun 17, 2020, 8:15:56 PM6/17/20
to Tyler Compton, Miguel Angel Rivera Notararigo, David Anderson, Christophe Meessen, golang-nuts
On Wed, Jun 17, 2020 at 10:55 AM Tyler Compton <xav...@gmail.com> wrote:
The reason why implicit conversions from []Foo to []Stringer isn't supported is because Foo and Stringer have different in-memory representations. Converting from a slice of one to a slice of another would be more expensive than one would expect an implicit conversion to be. At least, this is my understanding as to why.

Miguel Angel Rivera Notararigo

unread,
Jun 17, 2020, 10:53:15 PM6/17/20
to Ian Lance Taylor, Tyler Compton, David Anderson, Christophe Meessen, golang-nuts
Oh, it was a FAQ haha sorry about that and thanks Tyler and Ian :D

Axel Wagner

unread,
Jun 18, 2020, 3:49:40 AM6/18/20
to Miguel Angel Rivera Notararigo, Ian Lance Taylor, Tyler Compton, David Anderson, Christophe Meessen, golang-nuts
I'd also say that converting between []Foo and []Stringer isn't actually definable in a type-safe manner: https://blog.merovius.de/2018/06/03/why-doesnt-go-have-variance-in.html
Slices are writable as well as readable, so if you could convert []Foo to []Stringer you'd also have to say what happens when you assign a *different* Stringer to an element of it - and the only thing that could happen is a panic.

The memory-layout explanation never rang true to me. After all, we *do* allow converting between string and []rune, which has the same problem.

On Thu, Jun 18, 2020, 04:53 Miguel Angel Rivera Notararigo <ntr...@gmail.com> wrote:
Oh, it was a FAQ haha sorry about that and thanks Tyler and Ian :D

--
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.

Miguel Angel Rivera Notararigo

unread,
Jun 18, 2020, 10:21:25 AM6/18/20
to Axel Wagner, Ian Lance Taylor, Tyler Compton, David Anderson, Christophe Meessen, golang-nuts
Thanks, that is a great explanation :)

Ankur Agarwal

unread,
Jun 22, 2020, 4:41:58 PM6/22/20
to golang-nuts
I like the idea of generics, and having played around with them it definitely feels worth bringing into Golang. But, I agree with the author of this post and I don't think constraints on generics is needed.

I do understand the arguments that:
1. There's no implicit conversion between a value type and an interface, and constraints would make this possible, but i feel that it's at the expense of making the language unnecessarily complicated. Can we not just think through our design of our applications to either prevent this need or ensure that we build a slice of interfaces?
2. Being able to constrain on comparable values (using <,>,==,!=). Couldn't we create an interface which declares what we need (LessThan, MoreThan, etc)? I feel that if the language goes down this path, why not do the whole shebang and have operator overloading in go?

I've yet to really feel the need for either of these (although i'm not a veteran golang dev -- only 1 year with golang in a professional environment), but I have come across scenarios where generics would otherwise be useful... (and function overloading, but that's a whole different kettle of fish)

It's great that we've been given the chance to give some feedback :)

Ian Lance Taylor

unread,
Jun 22, 2020, 4:53:36 PM6/22/20
to Ankur Agarwal, golang-nuts
On Mon, Jun 22, 2020 at 1:41 PM Ankur Agarwal <aka...@gmail.com> wrote:
>
> I like the idea of generics, and having played around with them it definitely feels worth bringing into Golang. But, I agree with the author of this post and I don't think constraints on generics is needed.
>
> I do understand the arguments that:
> 1. There's no implicit conversion between a value type and an interface, and constraints would make this possible, but i feel that it's at the expense of making the language unnecessarily complicated. Can we not just think through our design of our applications to either prevent this need or ensure that we build a slice of interfaces?
> 2. Being able to constrain on comparable values (using <,>,==,!=). Couldn't we create an interface which declares what we need (LessThan, MoreThan, etc)? I feel that if the language goes down this path, why not do the whole shebang and have operator overloading in go?
>
> I've yet to really feel the need for either of these (although i'm not a veteran golang dev -- only 1 year with golang in a professional environment), but I have come across scenarios where generics would otherwise be useful... (and function overloading, but that's a whole different kettle of fish)
>
> It's great that we've been given the chance to give some feedback :)

I'm sorry, I'm not sure quite what you are saying. The reason for
constraints is to create a contract between the generic function and
its caller, so that far away code doesn't break unexpectedly. See
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#constraints
.

Are you talking about type lists, rather than constraints?

Ian

burak serdar

unread,
Jun 22, 2020, 5:01:25 PM6/22/20
to Ian Lance Taylor, Ankur Agarwal, golang-nuts
On Mon, Jun 22, 2020 at 2:53 PM Ian Lance Taylor <ia...@golang.org> wrote:
>
> On Mon, Jun 22, 2020 at 1:41 PM Ankur Agarwal <aka...@gmail.com> wrote:
> >
> > I like the idea of generics, and having played around with them it definitely feels worth bringing into Golang. But, I agree with the author of this post and I don't think constraints on generics is needed.
> >
> > I do understand the arguments that:
> > 1. There's no implicit conversion between a value type and an interface, and constraints would make this possible, but i feel that it's at the expense of making the language unnecessarily complicated. Can we not just think through our design of our applications to either prevent this need or ensure that we build a slice of interfaces?
> > 2. Being able to constrain on comparable values (using <,>,==,!=). Couldn't we create an interface which declares what we need (LessThan, MoreThan, etc)? I feel that if the language goes down this path, why not do the whole shebang and have operator overloading in go?

It does not make much sense to constrain a type to have less-then,
greater-than support in a language without operator overloading. There
is a finite list of types that support LessThan, so it is more
explicit and simpler to list the types you expect in a generic
function (think "i expect to receive a string or int" as opposed to "i
expect to receive a type that supports <". The second one will let you
pass a float64, which may be unexpected.) And I believe many Go users
are happier because there is no operator overloading.


> >
> > I've yet to really feel the need for either of these (although i'm not a veteran golang dev -- only 1 year with golang in a professional environment), but I have come across scenarios where generics would otherwise be useful... (and function overloading, but that's a whole different kettle of fish)
> >
> > It's great that we've been given the chance to give some feedback :)
>
> I'm sorry, I'm not sure quite what you are saying. The reason for
> constraints is to create a contract between the generic function and
> its caller, so that far away code doesn't break unexpectedly. See
> https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#constraints
> .
>
> Are you talking about type lists, rather than constraints?
>
> Ian
>
> --
> 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.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcXfn3fpbQ5RXhB0f6EaYgyZbphtod24G%3DjxtzfEuF9SEA%40mail.gmail.com.

Robert Engels

unread,
Jun 22, 2020, 6:05:05 PM6/22/20
to burak serdar, Ian Lance Taylor, Ankur Agarwal, golang-nuts
The list of types supporting LessThan is not finite, is unbounded. The list of types supporting < is finite. But < could of been viewed as syntactic sugar if the Go designers so chose.

> On Jun 22, 2020, at 4:01 PM, burak serdar <bse...@computer.org> wrote:
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAMV2Rqo72YaJm7uyZano8aSrZTAbv2iNH1Ofv4Xa67ZicUuWbQ%40mail.gmail.com.

burak serdar

unread,
Jun 22, 2020, 7:19:55 PM6/22/20
to Robert Engels, Ian Lance Taylor, Ankur Agarwal, golang-nuts
On Mon, Jun 22, 2020 at 4:04 PM Robert Engels <ren...@ix.netcom.com> wrote:
>
> The list of types supporting LessThan is not finite, is unbounded. The list of types supporting < is finite. But < could of been viewed as syntactic sugar if the Go designers so chose.

Actually, the list of types supporting LessThen contains only one
type: interface {LessThan() bool}

I didn't see in the draft design a limitation on the types that can be
listed in a type list. So can a type list contain an interface?

If that is the case, I will repeat my suggestion to take type lists
outside interfaces and make them their own construct. Maybe call it
something else, like typeconstraint:

typecontstraint T int, string, OtherType

Robert Engels

unread,
Jun 22, 2020, 8:06:40 PM6/22/20
to burak serdar, Ian Lance Taylor, Ankur Agarwal, golang-nuts
Why would you need to declare a type being int or string for instance? It seems it would only ever be because of built in operators or you would need type casting code to use them - which is what generics tries to avoid.

This is why Java’s boxed types and standard interfaces make generics far easier - and still performant due to JIT, intrinsics and inlining.

I am still uncertain why Go doesn’t take the same approach (only allow interface types) using static analysis at compilation time.

> On Jun 22, 2020, at 6:19 PM, burak serdar <bse...@computer.org> wrote:
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAMV2Rqo4mFU84Z2JNwi8Wk_vwW9EdCwrqn6NOJ3e68hdWFCgVA%40mail.gmail.com.

burak serdar

unread,
Jun 23, 2020, 12:04:20 AM6/23/20
to Robert Engels, Ian Lance Taylor, Ankur Agarwal, golang-nuts
On Mon, Jun 22, 2020 at 6:06 PM Robert Engels <ren...@ix.netcom.com> wrote:
>
> Why would you need to declare a type being int or string for instance? It seems it would only ever be because of built in operators or you would need type casting code to use them - which is what generics tries to avoid.

If all you need to do is ==, then having an int or string (and not
float64) makes sense.

I have this JSON processing library that lets me treat JSON data like
DOM, with object, array and value nodes. The value nodes contain
interface{}, but I know it can only be json.Number, string, and bool
right after decoding. It wouldn've been nice to constrain the types
the JSON doc accepts to these three, so when, say, at some point in
the program someone tries to put an int as value it doesn't accept it.

Ankur Agarwal

unread,
Jun 23, 2020, 7:35:06 AM6/23/20
to golang-nuts
Sorry, I wasn't clear in my last post.

> 1. There's no implicit conversion between a value type and an interface, and constraints would make this possible, but i feel that it's at the expense of making the language unnecessarily complicated. Can we not just think through our design of our applications to either prevent this need or ensure that we build a slice of interfaces?
Here i was suggesting that constraints might not be needed as we can use interfaces:

// Without generics
func Stringify(s []Stringer) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}

// With Constraints in generics
func Stringify(type T Stringer)(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}

> 2. Being able to constrain on comparable values (using <,>,==,!=). Couldn't we create an interface which declares what we need (LessThan, MoreThan, etc)? I feel that if the language goes down this path, why not do the whole shebang and have operator overloading in go?
Here i was suggesting that constraints isn't necessary when trying to compare two values, instead just use generics on interfaces:

// Without constraints
type LessThaner(type T) interface {
LessThan(T) bool
Get() T
}

func Smallest(type T)(s []LessThaner(T)) T {
smallest := s[0].Get() // panics if slice is empty
for _, t := range s[1:] {
if t.LessThan(smallest) {
smallest = t.Get()
}
}
return smallest
}

// With constraints in type list
func Smallest(type T constraints.Ordered)(s []T) T {
r := s[0] // panics if slice is empty
for _, v := range s[1:] {
if v < r {
r = v
}
}
return r
}

So my main argument is that constraints add an unnecessary dimension to generics, and it might be better to try and avoid them.

(apologies if this post appears twice, i posted a reply but nothing appeared after an hour)

- Ankur

teelin...@gmail.com

unread,
Jun 23, 2020, 12:34:09 PM6/23/20
to golang-nuts
Instead of doing the implicit conversion to the interface, why not relax the constraint and allow anything that implements the interface?


On Wednesday, June 17, 2020 at 1:37:35 AM UTC-7, David Anderson wrote:
The non-generic form _only_ accepts an argument of type []Stringer. If you have a []Foo, where Foo implements Stringer, that's not good enough. You will have to construct a []Stringer explicitly, and copy each Foo into that []Stringer. This is a fairly common gotcha with Go, where folks expect the implicit conversion to happen.

OTOH, the generic form will accept []Foo directly, since Foo satisfies the Stringer constraint.

- Dave

On Wed, Jun 17, 2020 at 1:20 AM Christophe Meessen <christop...@gmail.com> wrote:
Reading the document "Type parameters - Draft desing" of June 16, 2020 (https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md), I wonder if these two following function definitions are not equivalent and thus interchangeable

func Stringify(type T Stringer)(s []T) (ret []string)

func Stringify(s []Stringer) (ret []string)

If the constrain is an interface, wouldn’t it be the same as to use the interface as type ? How would it be different ?

--
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 golan...@googlegroups.com.

On Wednesday, June 17, 2020 at 1:37:35 AM UTC-7, David Anderson wrote:
The non-generic form _only_ accepts an argument of type []Stringer. If you have a []Foo, where Foo implements Stringer, that's not good enough. You will have to construct a []Stringer explicitly, and copy each Foo into that []Stringer. This is a fairly common gotcha with Go, where folks expect the implicit conversion to happen.

OTOH, the generic form will accept []Foo directly, since Foo satisfies the Stringer constraint.

- Dave

On Wed, Jun 17, 2020 at 1:20 AM Christophe Meessen <christop...@gmail.com> wrote:
Reading the document "Type parameters - Draft desing" of June 16, 2020 (https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md), I wonder if these two following function definitions are not equivalent and thus interchangeable

func Stringify(type T Stringer)(s []T) (ret []string)

func Stringify(s []Stringer) (ret []string)

If the constrain is an interface, wouldn’t it be the same as to use the interface as type ? How would it be different ?

--
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 golan...@googlegroups.com.

On Wednesday, June 17, 2020 at 1:37:35 AM UTC-7, David Anderson wrote:
The non-generic form _only_ accepts an argument of type []Stringer. If you have a []Foo, where Foo implements Stringer, that's not good enough. You will have to construct a []Stringer explicitly, and copy each Foo into that []Stringer. This is a fairly common gotcha with Go, where folks expect the implicit conversion to happen.

OTOH, the generic form will accept []Foo directly, since Foo satisfies the Stringer constraint.

- Dave

On Wed, Jun 17, 2020 at 1:20 AM Christophe Meessen <christop...@gmail.com> wrote:
Reading the document "Type parameters - Draft desing" of June 16, 2020 (https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md), I wonder if these two following function definitions are not equivalent and thus interchangeable

func Stringify(type T Stringer)(s []T) (ret []string)

func Stringify(s []Stringer) (ret []string)

If the constrain is an interface, wouldn’t it be the same as to use the interface as type ? How would it be different ?

--
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 golan...@googlegroups.com.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages