[generics] where to find predefined constraint aggregations?

153 views
Skip to first unread message

i3dmaster

unread,
Jun 24, 2020, 5:33:04 PM6/24/20
to golang-nuts
example:

type Key interface {
    type comparable, fmt.Stringer
}

type Value interface {
    type interface{}, copyable
}

type Container(type K Key, V Value) interface {
   Add(K, V) bool
}

type HashMap(type K Key, V Value) struct{ m map[K]V }

func (h *HashMap(K,V)) Add(k K, v V) bool {
    h.m[k] =v 
    return true
}

But of course, copyable isn't defined... So where to find some of these predefined constraints? Or if not, how could one define a such constraint?

Also how to specify constrains like

1. A type that I can call len() against it?
2. A type that I can range over it?

Thanks!

Ian Lance Taylor

unread,
Jun 24, 2020, 6:02:43 PM6/24/20
to i3dmaster, golang-nuts
Your message came through as white text on a black background that is
hard to read. On this mailing list, please send plain text as plain
text. Thanks.

The current generics design draft doesn't support the kind of
constraints you are looking for.

I'm not even sure what you mean by "copyable". All Go types can be copied.

Typically instead of writing generic type constraints like supports
len or range people write code that takes a slice of some generic
type.

Note that you are using type lists in a way that doesn't look right.
To write a constraint that is a combination of other constraints, you
should use embedding, as in

type Key interface {
comparable
fmt.Stringer
}

Ian

i3dmaster

unread,
Jun 24, 2020, 6:46:45 PM6/24/20
to golang-nuts


On Wednesday, June 24, 2020 at 3:02:43 PM UTC-7, Ian Lance Taylor wrote:
On Wed, Jun 24, 2020 at 2:33 PM i3dmaster <i3dm...@gmail.com> wrote:
>
> example:
>
> type Key interface {
>     type comparable, fmt.Stringer
> }
>
> type Value interface {
>     type interface{}, copyable
> }
>
> type Container(type K Key, V Value) interface {
>    Add(K, V) bool
> }
>
> type HashMap(type K Key, V Value) struct{ m map[K]V }
>
> func (h *HashMap(K,V)) Add(k K, v V) bool {
>     h.m[k] =v
>     return true
> }
>
> But of course, copyable isn't defined... So where to find some of these predefined constraints? Or if not, how could one define a such constraint?
>
> Also how to specify constrains like
>
> 1. A type that I can call len() against it?
> 2. A type that I can range over it?

Your message came through as white text on a black background that is
hard to read.  On this mailing list, please send plain text as plain
text.  Thanks.

Oh god, I am sorry about that. I was using new browser + black background and didn't realize it even decorated the message. 

 
The current generics design draft doesn't support the kind of
constraints you are looking for.

Is there or will there be a plan? Using generic types with [], map, etc can work on some use cases,
but there are likely also use cases where custom container types would more appreciated to abtract
or confine certain API surfaces. I wish Go authors wouldn't completely eliminate these possiblities.
 

I'm not even sure what you mean by "copyable".  All Go types can be copied.
Hm... I thought there are some aren't at least recommened to copy, e.g. Mutex, Cond, or any types that explicitly documented not to copy. 

Typically instead of writing generic type constraints like supports
len or range people write code that takes a slice of some generic
type.

Note that you are using type lists in a way that doesn't look right.
To write a constraint that is a combination of other constraints, you
should use embedding, as in

type Key interface {
    comparable
    fmt.Stringer
}

Interesting, maybe I wasn't still quite not understanding the draft spec... What's the difference here?

// Numeric is a constraint that matches any numeric type. 
// It would likely be in a constraints package in the standard library. 
type Numeric interface { 
   type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128 
}

Also, btw, it compiles just fine.


Ian

Ian Lance Taylor

unread,
Jun 24, 2020, 6:55:07 PM6/24/20
to i3dmaster, golang-nuts
On Wed, Jun 24, 2020 at 3:47 PM i3dmaster <i3dm...@gmail.com> wrote:
>
> On Wednesday, June 24, 2020 at 3:02:43 PM UTC-7, Ian Lance Taylor wrote:
>>
>> The current generics design draft doesn't support the kind of
>> constraints you are looking for.
>
>
> Is there or will there be a plan? Using generic types with [], map, etc can work on some use cases,
> but there are likely also use cases where custom container types would more appreciated to abtract
> or confine certain API surfaces. I wish Go authors wouldn't completely eliminate these possiblities.

Custom container types aren't going to support len or range.
Functions like len and range are built into the language, and only
work with language defined types. So if we want to abstract over
containers, we shouldn't start with len or range. We should start
with other mechanisms, and encourage container authors to implement
them, and figure out how to make them work with slices and maps.


>> I'm not even sure what you mean by "copyable". All Go types can be copied.
>
> Hm... I thought there are some aren't at least recommened to copy, e.g. Mutex, Cond, or any types that explicitly documented not to copy.

I see. That isn't a language restriction, though, it's just
documentation plus a convention for the vet tool. I'm not sure it
makes sense to define a constraint for it.


>> Note that you are using type lists in a way that doesn't look right.
>> To write a constraint that is a combination of other constraints, you
>> should use embedding, as in
>>
>> type Key interface {
>> comparable
>> fmt.Stringer
>> }
>
>
> Interesting, maybe I wasn't still quite not understanding the draft spec... What's the difference here?
>
> // Numeric is a constraint that matches any numeric type.
> // It would likely be in a constraints package in the standard library.
> type Numeric interface {
> type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128
> }
>
> Also, btw, it compiles just fine.

Using a type list, as in your Numeric example, means that the type
argument has to be one of the listed types. When you write

type Key interface {
type comparable, fmt.Stringer
}

it means that the type argument must be comparable or fmt.Stringer, or
some other type whose underlying type is comparable or fmt.Stringer.
But what you want is not a requirement that the type argument be
fmt.Stringer, but a requirement that the type argument implement
fmt.Stringer. And that is done by using an embedded interface.

Ian

Yongjian (Jim) Xu

unread,
Jun 24, 2020, 7:38:14 PM6/24/20
to golang-nuts
On Wednesday, June 24, 2020 at 3:55:07 PM UTC-7 Ian Lance Taylor wrote:
On Wed, Jun 24, 2020 at 3:47 PM i3dmaster <i3dm...@gmail.com> wrote:
>
> On Wednesday, June 24, 2020 at 3:02:43 PM UTC-7, Ian Lance Taylor wrote:
>>
>> The current generics design draft doesn't support the kind of
>> constraints you are looking for.
>
>
> Is there or will there be a plan? Using generic types with [], map, etc can work on some use cases,
> but there are likely also use cases where custom container types would more appreciated to abtract
> or confine certain API surfaces. I wish Go authors wouldn't completely eliminate these possiblities.

Custom container types aren't going to support len or range.
Functions like len and range are built into the language, and only
work with language defined types. So if we want to abstract over
containers, we shouldn't start with len or range. We should start
with other mechanisms, and encourage container authors to implement
them, and figure out how to make them work with slices and maps.

So... ok. I guess type list constraints is going to only work with builtin types then.

e.g. it would be useless (or maybe even wrong to write)

type ContainerLike interface {
   map, [], mypackage.Container
}

because mypackage.Container is simply just not going to work (even if it's implantation may just use the builtin map)

Will it work if mypackage.Container is an alias type of builtin map? e.g.

type Container(type K, V) map[K]V



>> I'm not even sure what you mean by "copyable". All Go types can be copied.
>
> Hm... I thought there are some aren't at least recommened to copy, e.g. Mutex, Cond, or any types that explicitly documented not to copy.

I see. That isn't a language restriction, though, it's just
documentation plus a convention for the vet tool. I'm not sure it
makes sense to define a constraint for it.
Sure, it might be fine if the language doesn't provide a builtin one, but if it is possible for API authors to 
define their own?

We could say

type Copyable interface {
   type !sync.*, !mypackage.Container
}

of course, there is no support for negative constraints yet... But just wanted to see we could possibly offer.
 


>> Note that you are using type lists in a way that doesn't look right.
>> To write a constraint that is a combination of other constraints, you
>> should use embedding, as in
>>
>> type Key interface {
>> comparable
>> fmt.Stringer
>> }
>
>
> Interesting, maybe I wasn't still quite not understanding the draft spec... What's the difference here?
>
> // Numeric is a constraint that matches any numeric type.
> // It would likely be in a constraints package in the standard library.
> type Numeric interface {
> type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128
> }
>
> Also, btw, it compiles just fine.

Using a type list, as in your Numeric example, means that the type
argument has to be one of the listed types. When you write

type Key interface {
type comparable, fmt.Stringer
}

it means that the type argument must be comparable or fmt.Stringer, or
some other type whose underlying type is comparable or fmt.Stringer.
But what you want is not a requirement that the type argument be
fmt.Stringer, but a requirement that the type argument implement
fmt.Stringer. And that is done by using an embedded interface.

Got it. I think I finally understand this part of the spec. They both are syntactically correct, but have a very different semantic. Good to know.
 

Ian
Reply all
Reply to author
Forward
0 new messages