Go Generics using interfaces - alternative take

329 views
Skip to first unread message

Arnaud Delobelle

unread,
Jun 16, 2020, 4:55:02 PM6/16/20
to golang-nuts
Hi everyone,

I noticed today the blog post about the revised Generics proposal.  What got me excited is this extract at the start:

The biggest change is that we are dropping the idea of contracts. The difference between contracts and interface types was confusing, so we’re eliminating that difference.

To me the lack of orthogonality between contracts and interfaces was the major issue with the previous proposal and I have been musing with ways of resolving it for a while, so I am very pleased that the proposal is going down this path!

Last month I decided to write a post about a way of introducing Generics into Go using interfaces,  which I called "Package Specialization".

Headline features are:

  • use interfaces to express parametric types;
  • no new keyword;
  • the only new syntax is “package specialization”: pkg(T=int, Y=*MyType).Func();
  • generic code which doesn’t use the package specialization syntax is already valid Go.

I was toying with the idea of submitting it here for feedback, to try to move the discussion in that direction.  Well, I guess today's updated proposal steals my thunder! Essentially this is the same idea, but with a different manifestation in the language - I thought that submitting it anyway may provide a useful comparison point to help gauge the updated proposal better.

Note that sadly, I do not have much time to polish a proposal or to follow discussions in general on this or other golang-related discussion lists, so I apologize in advance if I am inadvertently re-hashing ideas that have been put forward and dismissed in the past, or if my blog post is a little low on detail (it was meant to be an exploration of a possible approach).

Kinds regards,

Arnaud Delobelle

Ian Lance Taylor

unread,
Jun 17, 2020, 1:10:08 AM6/17/20
to Arnaud Delobelle, golang-nuts
Thanks. I hadn't seen that before.

A difficulty with package specialization is when you want to write a
List(List(int)). That is, a List where each element of the List is a
List(int). Or, when you want to write a transformation function as in
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#list-transform.
It would seem to require some modification of how imports work in Go,
and that leads into considerable complexity. See also
.https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#why-not-put-type-parameters-on-packages
.

Ian

tomj...@gmail.com

unread,
Jun 18, 2020, 11:58:37 PM6/18/20
to golang-nuts

Hi Everyone,


I saw the well written 16 June 2020 blog post The Next Step for Generics by Ian Lance Taylor and Robert Griesemer too.


I agree, many people have said that Go needs generics, but we don’t necessarily know exactly what that means. If it means introducing generics into Go using interfaces as suggested by Arnaud Delobelle maybe that's OK. But If it means doing something like C++, Java, Erlang, Perl, Python, Lua etc then I hope it never ever happens. 


I also agree that it's important to ask if adding generics feels like Go? I thought Go was a reaction to the  C++, Java, others etc programming languages of the 1990's where their use of generics added significant complexity to both semantics and implementation. 


I know for some Go users having no generics sucks, but in my limited experience this happens rarely. Maps, slices, channels and interfaces are built in generic data structures and address many of my common use cases. 


Go is the first language I've learnt. What I really liked about it is its “less is more” approach. So far I'm not missing user-defined generics. I have gotten on fine without it. But if it is added at some point I will be forced to implement it because everyone else is, and that is something I am not looking forward to.


I see NOT having user-defined generics as a critical positive feature of Go and not something that is missing. Rather than focusing on what we can add to Go, maybe we should focus instead on how we can further improve Go by what else we can remove from Go?


Tom

Arnaud Delobelle

unread,
Jun 22, 2020, 2:55:10 AM6/22/20
to golang-nuts
I was planning to read the updated proposal in more details before replying, but I haven't been able to find the time sadly, so I'm doing it anyway :), mostly inline.  There are a couple of general points I'd like to put forward first.

- What I'm trying to push towards is not my particular approach, but more simplicity while retaining enough usefulness (that is, being a good fit for the established good use cases for generics in Go).  I think that when you explore how to introduce generics into Go, it's important to remember how effective it has been without generics (and partly because of its lack of generics), and to try not to upset that balance.

- There is one aspect of the current proposal that makes me think that we can find a better manifestation of generics in Go.  To take the one of the simplest examples, the Stringer example:

type Stringer interface {
String() string
}

Here is the generic Stringify implementation

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

Here is the "plain" non-generic one

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

The two implementations above express the same intent, even have the same body, but they are not the same thing!  Obviously you can use Stringify(Stringer) to obtain the second one from the first but to me it feels like we are missing a trick, a better perspective that would make this simplest of examples more natural.

- shortly after the contracts proposal was published, I found that I was unhappy with the overlap between contracts and interfaces, wrote an explanation of the problem and imagined a rebalancing of the proposal which is in essence the new one (ie. interfaces can be generic).  I think it's useful because it presents a rationale for "transforming" the earlier contracts proposal into the new interface-based one, so I've posted it here: https://arnodel.github.io/marooned/go/generics/2020/06/21/go-against-contracts.html

Kind regards,

Arnaud Delobelle

On Wednesday, 17 June 2020 at 06:10:08 UTC+1 Ian Lance Taylor wrote:
On Tue, Jun 16, 2020 at 1:54 PM Arnaud Delobelle
<arnaud.d...@sparx.co.uk> wrote:
>
> I noticed today the blog post about the revised Generics proposal. What got me excited is this extract at the start:
>
> The biggest change is that we are dropping the idea of contracts. The difference between contracts and interface types was confusing, so we’re eliminating that difference.
>
> To me the lack of orthogonality between contracts and interfaces was the major issue with the previous proposal and I have been musing with ways of resolving it for a while, so I am very pleased that the proposal is going down this path!
>
> Last month I decided to write a post about a way of introducing Generics into Go using interfaces, which I called "Package Specialization".
>
> Headline features are:
>
> use interfaces to express parametric types;
> no new keyword;
> the only new syntax is “package specialization”: pkg(T=int, Y=*MyType).Func();
> generic code which doesn’t use the package specialization syntax is already valid Go.
>
> The full post can be read here: https://arnodel.github.io/marooned/go/generics/2020/06/06/go-spec2.html
>
> I was toying with the idea of submitting it here for feedback, to try to move the discussion in that direction. Well, I guess today's updated proposal steals my thunder! Essentially this is the same idea, but with a different manifestation in the language - I thought that submitting it anyway may provide a useful comparison point to help gauge the updated proposal better.
>
> Note that sadly, I do not have much time to polish a proposal or to follow discussions in general on this or other golang-related discussion lists, so I apologize in advance if I am inadvertently re-hashing ideas that have been put forward and dismissed in the past, or if my blog post is a little low on detail (it was meant to be an exploration of a possible approach).

Thanks. I hadn't seen that before.

A difficulty with package specialization is when you want to write a
List(List(int)). That is, a List where each element of the List is a
List(int).

That would be

     list(T = list(T = int).List).List,

which I agree is not the most palatable.  Of course one could defined an intermediary type

  type intList = list(T = int).List
I guess to do it literally you would do something like this.

package transform

type T1 interface {}
type T2 interface {}
func Transform(lst *list(T1).List, f func(T1) T2) *list(T2).List {
ret := &list(T2){}
it := lst.Range()
for {
if v, ok := it.Val(); ok {
ret.Push(f(v))
}
if !it.Next() {
break
}
}
return ret
}

To me it becomes apparent that it's not the right way of doing this though as the implementation of Transform doesn't really depend on the fact that we are dealing with lists, instead it relies solely on the iteration interface for the source and the "pushing" interface for the destination.  So I'd probably have it as something like:

--- transform/transform.go ---
package transform

type T1 interface{}
type T2 interface{}

type S interface {
    Next() bool
    Val() (T1, bool)
}

type D interface {
    Push(T2)
}

func Transform(src S, f func(T1) T2, dst D) {
    for {
        if v, ok := src.Val(); ok {
            dst.Push(v)
        }
        if !src.Next() {
            return
        }
    }
}

--- main.go ---
// l1 is a list of ints
l2 := &list(float64).List{}
transform(...).Transform(l1.Range(), l2, func(n int) float64 { return float64(n)})

That transform package doesn't require special generic syntax to be written (or understood), and I guess that was part of my aim.


It would seem to require some modification of how imports work in Go,
and that leads into considerable complexity. See also
.https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#why-not-put-type-parameters-on-packages

I must admit that although I do know how imports work in Go, I don't know anything about how they are implemented.  I did try to think this through though and outlined a way to understand the semantics of specialized packages, which I thought was acceptable.  To me there are also interesting questions about importing "generic entities" and specializing them with the same types in different packages under the current proposal (but I haven't been able to verify what the proposed solution to these issues are).
.

Ian 
Reply all
Reply to author
Forward
0 new messages