Hi there

I am aware that the proposal to add generics has been accepted <https://github.com/golang/go/issues/43651#issuecomment-776944155>, so the discussion of whether or not Go will get generics is answered. For better or for worse.

I worked on a different approach since a few years, not very intensive, and in spite of this fact, I now want to tell you about:

Binding interface types on import <https://github.com/leiser1960/importbinding/blob/master/README.md>

See in: https://github.com/leiser1960/importbinding/blob/master/README.md

I did not speek up yet, because it simply was not ready for discussion in my eyes.


Why do I think the accepted proposal is not good enough?

Technically it seem sound and consistent to me and has all the bells and whistles you would expect. And all the Features. And close enough to the way other languages define generics.

So what do i dislike?

Hmmm... Let me answer another question first:

What do I love about Go?

Its *simplicity. *And Simplicity is Complicated <https://www.youtube.com/watch?v=rFejpH_tAHM>

And that is IMHO the problem of the accepted generics proposal, the lack of simplicity.

To explain this, let me start with a language design question:

    What is needed for adding "monomorphic generic types" to Go?

Go already has a polymorphic generic type mechanism: "interface types".

So you have to start from this (as the accepted proposal does) and add to things:

    - A way to define type parameters.

    - A way to bind the type parameters to concrete types at compile time

The accepted proposal does so by:

    1. Adding positional type parameters to type and function definitions

    2. Adding a syntax for binding concrete types to the type parameters on use of the types and functions.

With the proper brackets it is readable. And of course there are more goodies in the propsal such as:

    - An extension to the descriptive power of  "interface types".

    - type inference rules to eliminate the need of the additional type parameters in certain cases.

Both are great for functional libraries such as "sort.go", but does not help for container types such as "container/list.go". And the proposal is long because any generic type mechanism is complicated to implement and tricky to integrate in the language.
This  is the obviously complicated part.

But the proposal lacks the simplicity and elegance of for example:

    - the way Go integrates inheriting method from a typ by simply omitting the attribute name in a struct definition

    - the way Go defines the types of numerical constant expressions for the purpose of type inference

incredibly simple to use and explain. No need for an additional syntactic element at all.
And completely different from the way other languages treat this.

The accepted proposal more or less follows the syntactic path taken since Ada, over C++ and Java and all the other languages.

It this good enough?

Can we do better?

I think so. But How? Remember we need to do two things:

    - A way to define type parameters.

    - A way to bind the type parameters to concrete types at compile time

Is suggest doing this on the package level, not the individual types or functions:

    1. Use named interface types as implicit type parameters of a package

    2. Bind the named types on import

With the first I simply mean any declaration of the form:

    type ElementType interface {}

e.g. The type Locker <https://golang.org/pkg/sync/#Locker>in sync.go.

You do not find such a declaration in "containter/list.go <https://golang.org/pkg/container/list>" . But it would be simple task to add such a line and replace all existing occurences of "interface{}" by "ElementType". Not changing the behaviour at all.

As a go proverb says: interface nothing says nothing <https://go-proverbs.github.io/>

But after we named it "ElementType" we can get a hold of it and bind it in the package we intend to use e.G. a "list of strings":

    import "container/list"  type ElementType string

augmenting the existing "import" clause by this type binding. We say:

    We bind (the binding) type "string" to (the bound type) "list.ElementType".

    We bind the type string to the type list.ElementType in the import of package list.

It is as simple as this.

But what does this mean?

It means two things:

    1. binding type must implement the bound type.

    2. the bound type is treated as if defined by:

            type BoundType BindingType

    in the imported package.

1. is trivial in our example, because interface{} is implemented by any time.

2. is trivial in our example, because "string" is a standard type.

The general case is not so trivial, think of the binding type being defined locally and private to the package. I tried to give a semantic description in my definition in Binding interface types on import <https://github.com/leiser1960/importbinding/blob/master/README.md> but it is not as elaborate as the accepted proposal. The semantics is defined by a set of program transformations back to a Go 1 program.


Is this *simplicty*?

I think so, ok not as simple as inheritance, because I have to add a syntactic element: The type binding on Import.
But definitely a simpler than the accepted proposal.


Last features. If you need two different bindings in one package, say a list of string and a list of int you may use:

    import "container/list" intlist  type ElementType = int

    import "container/list" stringlist type ElementType = string

And then simply pick either the package name: "intlist" or "stringlist" e.g.

    myintlist := intlist.New()

    mystringlist := stringlist.New()

This makes myintlist.Value of type int, whereas mystringlist.Value is of type string.

And if you think of the type "Map" in "sync.go <https://golang.org/pkg/sync/#Map>":  You need a way to bind more than one type on a single import: sync.Key and sync.Value:

    import "sync.go" type Key string; Map mySyncdata

But now we have all of the syntax by example. Simple to use.
You may not bind all "named interface types" on the import, the above does not bind the Type "Locker <https://golang.org/pkg/sync/#Locker>", so the "Cond <https://golang.org/pkg/sync/#Cond>" type remains polymorphic.

The definition of a generic package is go 1 compatible.
You only have to "name" the parameter types for use by type binding imports. Which improves the documentation as well.

Not convinced? Lets move from Go 1 polymorphic generics to Go 2 monomorphic use, again using the "container/list.go" example.

It requires two sorts of changes:

    1. Add the required type binding to the import clause, see above

    2. delete the type casts needed for accessing the "Value" Attribute of the "Element" struct:

       if "" = (string)mystringlist.Value {

       has to be changed to:

        if "" = mystringlist.Value {

Try the same with the accepted proposal.

But a last word:

Simplicity is Complicated <https://www.youtube.com/watch?v=rFejpH_tAHM>

So behind the scenes there is more to it:

    - there is an additional restriction for "named interface types" necessary in order to allow them to be used as type parameters.

    - there are new semantic hurdles because of the type parameters being on the package level.

    - I did not describe any "type inference rules" yet, but it should be possible to add this for typical functional libraries such as "sort.go <https://golang.org/pkg/sort/>".

   - type bindings my even be transitive, if the binding type is itself a exported interface type.

So the a full fledged proposal may end up as long as the accepted proposal. That is the complicated part anyway.

But it is this the kind of *simplicity *I want to see when talking about monomorphic generic types. Perhaps someone can do even better?

Yours Martin Leiser


--
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/6d48e04e-eade-6dc5-fb05-f1c6a7151a43%40gmail.com.

Reply via email to