[generics] question regarding package level generic type declarations

238 views
Skip to first unread message

Markus Heukelom

unread,
Jul 17, 2020, 4:55:40 AM7/17/20
to golang-nuts
The authors of the current generics proposal mention that they looked into doing generics only on package level:(https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#why-not-put-type-parameters-on-packages)

I understand the reasons what it wasn't pursued further. 

Did the authors investigate the possibility of declaring generic types on a package level instead of per type/function? Ie.

generic T {}

func Max(a, b T) T {}
func Min(a, b T) T {}

I wrote down an idea on this, that is admittingly a bit wild (or naive) or maybe impossible from a parsing perspective. But I'd like to validate the main idea behind it. 

My whole attempt was based on the observation that most packages that will include generic functionality, will only really need one or maybe a couple of generic type constraints. 

For example, if you look at a hypothetical statistics package, there will be many, many functions that compute stuff on slices of T, where T must be a number. So there will be many functions like:

type Number interface { // using currently proposed generics
type int, float64, // etc
}

func Average(type T Number)(values T...) T {} // using currently proposed generics
func Median(type T Number)(values T...) T {} 
func Binonimal(type T Number)(values T...) T {} 
// etc etc etc

My point is that the "(type T Number)" part in all the declarations / definitions become really redundant from a human perspective, just because a package will naturally (should?) only use 1 or at most a couple of generic types. That would make it, at least theoretically, possible to write something like

type Number interface {
int, float64, // etc
}
generic T Number

func Average(values T...) T {}
func Median((values T...) T {} 
// etc

Coincidentally, the builtin language sort of uses this idea in its documentation.

Another (theoretic) possibility would be something like this:

generic T Number ( // analog to multiple const or var declarations
func Average(values T...) T {}
func Median((values T...) T {} 
)

My question is: did the authors investigate something like this, or is it plainly impossible or undesired?  

For those interested, my proposal can be found here: https://github.com/golang/go/issues/39716. I use the key word "meta" there instead of "generic".

Ian Lance Taylor

unread,
Jul 17, 2020, 2:26:42 PM7/17/20
to Markus Heukelom, golang-nuts
Thanks for the note.

Yes, we looked at ideas like that. It's unclear how to handle this
area at the point where we want to instantiate the generic function or
type. How do we pass the type argument? If there is a single generic
type parameter it's not so bad, but there are many simple examples
that require multiple type parameters, such as the Map function on
slices:

func Map(type T1, T2)(s []T1, func(T1) T2) []T2

There should be some natural way for the caller to write Map(int,
string) to get a value whose type is

func([]int, func(int) string) []string

If generics are defined at the package level, how do we do that?

If the answer is that you specify the type arguments when you import
the package, then how do handle List(List(int))?

Ian

Markus Heukelom

unread,
Jul 18, 2020, 7:01:25 AM7/18/20
to Ian Lance Taylor, golang-nuts

Thanks for the answer.

Concerning only the current proposal, is there a reason why we shouldn't allow specifying the parametric types for a whole bunch of functions / types at once?

Like:

generic (type T) (

func Average(value []T) {...}
func Median(value []T) {...}
// etc
)

generic (type T1, T2) (

func Map(s []T1, func(T1) T2) []T2
func BiMap(s []T1, func(T1) T2) []T2
//...
)


All things within generic () would have the same type parameter signature. It would be a bit similar to how var and const declarations work. It would remove a lot of redundancy and parenthesis.

To make things symmetric, a single generic function would have to be specified with something like this

generic (type T1, T2) func Map(s []T1, func(T1) T2) []T2.

type Vertex generic (type T) struct {
 Coords [3]T
}

Ian Lance Taylor

unread,
Jul 18, 2020, 9:15:12 PM7/18/20
to Markus Heukelom, golang-nuts
On Sat, Jul 18, 2020 at 4:00 AM Markus Heukelom
<markus....@gain.pro> wrote:
>
> Concerning only the current proposal, is there a reason why we shouldn't allow specifying the parametric types for a whole bunch of functions / types at once?
>
> Like:
>
> generic (type T) (
>
> func Average(value []T) {...}
> func Median(value []T) {...}
> // etc
> )
>
> generic (type T1, T2) (
>
> func Map(s []T1, func(T1) T2) []T2
> func BiMap(s []T1, func(T1) T2) []T2
> //...
> )
>
>
> All things within generic () would have the same type parameter signature. It would be a bit similar to how var and const declarations work. It would remove a lot of redundancy and parenthesis.
>
> To make things symmetric, a single generic function would have to be specified with something like this
>
> generic (type T1, T2) func Map(s []T1, func(T1) T2) []T2.
>
> type Vertex generic (type T) struct {
> Coords [3]T
> }


How do we instantiate these generic types and functions? If a list of
generic type parameters is factored out, do we have to supply all of
them even if a particular function only uses one of them?

Also, while this may seem like a trivial question, how do we indent
within a generic block?

(To answer your question, we did actually look at syntactic approaches
along these lines for a while, but we eventually moved on to the ideas
presented in the design draft. Not because this idea is terrible, but
because we felt the approach used in the design draft was a better fit
for the language.)

Ian

Markus Heukelom

unread,
Jul 19, 2020, 3:58:03 AM7/19/20
to golang-nuts

If you need different type parameters lists, use multiple groups:

package collection

generic (type T) (

type List struct {
root *Node(T)
}

type Node struct {
prev, next *Node(T)
}

func (l *List(T)) InsertAfter(after* Node(T))
)

generic (type T1, T2) (
MapList(l List(T1), mapFn func(value T1) T2) List(T2)
ReduceList(l List(T1), init T2, accFn func(cur T2, val T1) T2) T2
)

generic (type T) Add(a, b T) { // not grouping is also allowed of course
}

func TestStuff() {
myMap := BiMap(int,string) // personally I really see the point in using <> or [] as this looks like a function call
myList := LinkedList(float64)
}
Syntax is a sketch. Please don't get me on "generic" key word etc :) it's not the point here.

Indentation: don't think there is a major issue here, it can just be indented. Indentation is automatically done anyway by most setups anyway. Or we don't indent. As long as it is consistent and automatic.

I think the grouping is could also especially helpful in the documentation, to get rid of redudant "noisy" type parameter lists.  

Manlio Perillo

unread,
Jul 21, 2020, 8:52:55 AM7/21/20
to golang-nuts
What about:

      package example(T1, T2)

This declares T1 and T2 as package level types.

  import "example"(int, float)

This imports the example package with T1 as int and T2 as float.

The limitation is that you need to import the same package multiple times for different T1 and T2.


Manlio

Ian Lance Taylor

unread,
Jul 21, 2020, 11:15:00 PM7/21/20
to Manlio Perillo, golang-nuts
On Tue, Jul 21, 2020 at 5:53 AM Manlio Perillo <manlio....@gmail.com> wrote:
>
> What about:
>
> package example(T1, T2)
>
> This declares T1 and T2 as package level types.
>
> import "example"(int, float)
>
> This imports the example package with T1 as int and T2 as float.
>
> The limitation is that you need to import the same package multiple times for different T1 and T2.

What about list(list(int))?

If we have to import a package, and then use a type from that package,
and then import another package, well, that's fairly different from
how Go works today.

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/40412f47-678d-4850-8344-97d86a964e42n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages