A proposal for generic in go

2094 views
Skip to first unread message

xingtao zhao

unread,
Jun 14, 2016, 9:04:05 PM6/14/16
to golang-nuts
Here is my proposal for generic in go: https://docs.google.com/document/d/1nO7D15c2B3eq2kF62C0yUs_UgpkyPL2zHhMAmlq1l98/edit?usp=sharing

Many parts has not been finished, and just initial thoughts. In the proposal, I want to keep back compatibility. And I try to add the orthogonal feature only and keep the language still simple enough. 

Please add your comments on it. Hope it is useful and could inspire some new features in go 2.0

Thanks!

Ian Lance Taylor

unread,
Jun 14, 2016, 9:22:08 PM6/14/16
to xingtao zhao, golang-nuts
Have you read the generics proposals linked from
https://golang.org/issue/15292 ?

Ian

xingtao zhao

unread,
Jun 15, 2016, 9:43:59 PM6/15/16
to golang-nuts, zhaox...@gmail.com
I read your proposals now. My proposal is very similar to your proposal of type parameter - in fact, they are almost identical. But in my proposal, I am not going that far as in your proposal. I did not include the operators, thus all generic functions can only access the unknown type through interface it satisfied. In my proposal, I am not allowing to access the field of an unknown type. 

Another valuable part my proposal added in the generic struct. I think this is different from the implementation in your proposal. Your proposal convert everything to interface method calls. While in my proposal, we have a special memory layout and padding algorithm, which make access the field of a struct with unknown type very cheap - almost identical to access an element in a slice/array.

And in my proposal, we do not need to change the compiler too much, and do not need to slow down the complier speed a lot.

Anyway, looking forward to any comments for the valuable part in my proposal. Thanks! 

parais...@gmail.com

unread,
Jun 16, 2016, 7:02:16 AM6/16/16
to golang-nuts
A good compromise would be to only implement "parametric" functions. append and make are" parametric" functions as they know what is their return type at compile time , no matter what type the array/slice argument is . That way people who don't want a generic type are happy as no generic type is introduced, and people who want to write functions like push/pop/map on arrays of any type in a type safe way. 

Go is full of compromises of that sort so it shouldn't be an "all or nothing" debate. 

Aside from the implementation, the most difficult matter is choosing the right syntax, such as

    func XOR<T>(array1 []T,array2 []T)array[]T{
        // implementation
    }

So yes, people would not be able to define their own type of container (set,queues,binary trees,...). But all these containers can be build from array or map structures and methods specific to each data structure could now be written in a "generic" way. 

At the end of the day we all want the same thing, compile time type safety, no more, no less.

andrew...@gmail.com

unread,
Jun 20, 2016, 3:49:06 PM6/20/16
to golang-nuts, parais...@gmail.com
As for me then everything is much more simple if Go language (as the starting point) will  introduce a non-reified generics.
Non reified generics does not requires a complex runtime support (static code which was generated by the compiler will be enough).
Type checking of the type arguments performed at the compile time on the calller side (sender code).
The callee side (receiver code) remains unchanged as it is currently is.
This has some benefits:
1. This is a starting point (but not a no dead end) and point for a later enhancements
2. Does not requires significant changes in the compiler (codegen)
3. Does not requires significant changes in the runtime (engine)
4. Does not requires significant changes in the type system (just to add a few additional fields into the interface type presentation)
5. Does not requires significant changes in the reflection (just to adapt for slightly extended type presentation)

Because they (generic types) will be not reified on the callee side (receiver code) then compiler can generate the same code as it generates currently.
Only callers side will perform generic type checks before assignments of values with generic types and vice versa (to variables, constants, function and methods arguments).

That is.

type Foo<K, V> struct {
}

func (s *Foo) Get(key K) (val V) {
  // Here is generated unchanged code
  // The same as for non generic use
}

func some() {
  foo := Foo<string, int>{}

  // Generated by the compiler
  // _typ := _getRTypeOf(foo)
  // _typ.isGeneric = true
  // _typ.genParams = [2]_GenParam
  // _typ.genParams[0] = &_GenParam{key: "K", val: _getRType(interface{}}
  // _typ.genParams[0] = &_GenParam{key: "V", val: _getRType(interface{}}
  // _typ.genArgs = [2]*_RType{}
  // _typ.genArgs[0] = _getRType(string)
  // _typ.genArgs[1] = _getRType(int)
  // Also compiler statically performs type check bounds of the type arguments to type parameters assignments (if type bounds specified)

  var key interface{}
  key = bool

  // Generated by the compiler (safe guard)
  // _temp0 := key
  // _checkGenericArg(_typ, 0, temp0)
  // _temp1 := foo.Get(key)

  val := foo.Get(_temp0)
}

That is, the compiler always (if it cannot compute the type at compile time and cannot to check them statically) inserts "safe guards" type check before any kimd of an assignments.
The `_checkGenericArg(_typ, index, key)` also performs the checks to type parameter bounds (eg. for Foo<K string, V int>) for a given index of the type parameter and argument.

Callee code does not performs any type checks on incomming parameters.
It always think that incomming parameters has specified type.
That is, if type bounds ommitted then specified type is interface{}
If type bounds specified then specified type is itself

Eg.

type Type1<E> struct {
}

type Type2<E int> struct {
}

func(t *Type1) foo(val E) {
}

func(t *Type2) foo(val E) {
}

Generated as the following (that's easy)

func(t *Type1) foo(val interface{}) {
}

func(t *Type2) foo(val int) {
}

andrew...@gmail.com

unread,
Jun 20, 2016, 4:34:15 PM6/20/16
to golang-nuts, parais...@gmail.com, andrew...@gmail.com
Overhead at runtime will be not very big;
1. Generate each time an instance of specific generic type with arguments
2. Perform generic specific type checks before any kind of an assignements

1. The first overhead can be reduced (eleminated) by the static code generation of specified generic types with storing them in special place

Eg.

foo := Foo<string, int>{}

Since this code is static (which means that at runtime the arguments (string and int) remains unchanged then this type (Foo<string, int>) can be canonicalized (I can also say "hashed" but canonicalized is more precisely). pre-generated by the compiler and stored (to avoid re-creating it each time at runtime) for further use.

2. The second overhead can be reduced (eleminated) by the static code generation when compiler can compute types (used in assignments).

Since specific generic type is know at compile time then generic type checks (the same as regular, non generic) can be performed at compile time

Micky

unread,
Jun 20, 2016, 7:54:42 PM6/20/16
to xingtao zhao, golang-nuts
It's never going to happen! To quote the powers-to-be, "The language is done".
Good luck!
> --
> 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.
> For more options, visit https://groups.google.com/d/optout.

Rodrigo Kochenburger

unread,
Jun 20, 2016, 8:15:29 PM6/20/16
to Micky, xingtao zhao, golang-nuts
Micky, I'm not sure where you're quoting from but they never said it's never gonna happen.

From the FAQ: "Generics may well be added at some point." and "This remains an open issue".


xingtao zhao

unread,
Jun 20, 2016, 8:29:26 PM6/20/16
to golang-nuts, parais...@gmail.com, andrew...@gmail.com
That was my original thought. While later I realized that we still need some parameter check in the callee code to satisfy the interface matching (the generic version of each function in my proposal). And we need special assembler function for dynamic interface cast.

andrew...@gmail.com

unread,
Jun 21, 2016, 3:24:12 AM6/21/16
to golang-nuts, parais...@gmail.com, andrew...@gmail.com
>> While later I realized that we still need some parameter check in the callee code to satisfy the interface matching (the generic version of each function in my proposal).

A am sorry but your judgements is not correct.
Here is a proofs:
1. It is redundant to perform check of the correctness of the assignments after when assignement already made.

Eg.

val i int
i = 0
// It is redundant to perform check the correctness of the assignments of `i` after when assignement of `i` already made.

2. Fucntion is a callable piece of code which CAN have initial variables (function parameters) which SHOULD be assigned (via function arguments) before function invocation.

Of course, these parameters can be explicit and implicit.
Eg. receiver of the method is an implicit parameter but it still a parameter (function variable which would be assigned before function invocation implicitly by the compiler, or by the reflection construction).

That is, this code is equilvalent.

var i int
i = 0
// other code, `i` already assigned

func foo(i int) {
  // other code, `i` already assigned
}


3. Also you should to know that in a generic type system (type sytem with the generic types) each type is potentialy a generic type.
Here is a proof.

Each type in a generic type system looks like a function.
Type can have a [0..~] number of parameters which are declared by the type declaration and whose parameters can be assigned by the type arguments. If arguments omitted then parameters assigned to default values (types).


The same as the funtions.

// Declare type with 2 parameters, K and V
type Type<K, V> {}

// Declare type with 2 bounded parameters, K and V
type Type1<K string, V int> {}

// Instantiate new type with a specified arguments (the same as the function invocation)
// For simplicity we can imagine the following auto generated code
// if *__staticType1021 == nill {
//   *__staticType1021 = __createGenericType(__getType(Type1), __getType(string), __getType(bool))
//  That is, call to __createGenericType(*t RType, args... *RType)
// }

// foo := __newobject(*__staticType1021)

foo := Type1<string, bool>()

Another story when we use generic type on the callee side.

func (t *Type1) foo(key K) V {
  // Here compiler always assume that the type of `t` is a generic type `Type1` with a correctly assigned type arguments.
  // Assigned arguments does not need to re-check 
  // ___type0 := __getTypeOf(t)
  // Here is enough (since type are generic) rely only on that the each type argument bound to the type parameter
  ...
}


Henry

unread,
Jun 21, 2016, 4:13:53 AM6/21/16
to golang-nuts
I think we should carefully consider whether generics is worthy enough to be added into the language. It is easy to add a feature, but nearly impossible to remove one without breaking the backward compatibility promise.

One thing for sure is that we are in the middle of a paradigm shift. I am seeing more and more codes blending OOP with elements of functional programming. Some OOP purists go as far as avoiding getter/setter and use DTO to allow data editing, which to me it looks like a separation of data and function in procedural programming. Whatever the latest trend is, we shouldn't let Go become another C++ by blindly following trends. Everything must be thought of carefully.

I was one of the generics supporters for Go in this forum, but now I am not so sure.

andrew...@gmail.com

unread,
Jun 21, 2016, 4:45:08 AM6/21/16
to golang-nuts
>> I was one of the generics supporters for Go in this forum, but now I am not so sure.

But why?
Generic types is the same types as and the regular types.
Difference only in that the regular types does not have a parameters but generic types are always have at least one parameter.
This is why they are called generic (universal) types.
The same as you don't worry about that the function can have a parameters why you should worry about that the type can have a parameters?
When for types allowed to have parameters then this get a some benefits:
1. Generice types can be "configured" by some requirements (by their parameters)
2. Generice types can have a restrictions (eg. each parameter can have its own upper type bounds)
3. Generic types are universal types

Eg. Trivial example

type KeyValuePair<K, V> struct {
  key K
  val V
}

You can use this type everywhere since it is universal type.

Below is not the same type (because in generic type system K != V, but here interface{} == interface{}):

type KeyValuePair struct {
  key interface{}
  val interface{}
}

Because power of the generic types in that the they are variative:
- Covariant
- Contrvariant
- Invariant

And they, of course, can have a parameters.

Henry

unread,
Jun 21, 2016, 6:01:26 AM6/21/16
to golang-nuts, andrew...@gmail.com
I am not saying that generics is bad, but I am questioning whether generics is necessary.

Micky

unread,
Jun 21, 2016, 7:53:48 AM6/21/16
to Rodrigo Kochenburger, xingtao zhao, golang-nuts
I guess that's for diplomatic reasons. If you watch talks of core
contributors then I believe you'll know what I mean :)

andrew...@gmail.com

unread,
Jun 21, 2016, 8:45:21 AM6/21/16
to golang-nuts, andrew...@gmail.com
>> I am not saying that generics is bad, but I am questioning whether generics is necessary.

Please, do not panic.
If you worry about the following things:
- Generated code will grow when used generics
- Generated code will be more complex when used generics
- Execution performance will slow down when used generics
- Memory consumption will grow when used generics

I can say you.
- Generated code will NOT grow when used generics
- Generated code will NOT more complex when used generics
- Execution performance will NOT slow down when used generics
- Memory consumption will NOT grow when used generics

They are so arranged that they are universal for all uses cases and does not require a significant changes in the following things:
- Compilers
- Runtimes
- Reflections

Most of the type checks will be performed at the compile time only because the Go language is statically typed language.
Most of the type instances (instances of the generic types which will be created at runtime) will be stored for the further reuse (they will be not created again and again but they will retrieved from the hidden static variables, so called lazy getters).

Only when used reflection then they (newly created type instances) can get some overhead but just a little bit more then with non-generic types.

Generic types is not evil, nor complex.
They are still the same types but only with the parameters (this is single kind of complexity which for me is not complexity at all) and they are variative (but this is not a complexity this is just a rules).

Ian Davis

unread,
Jun 21, 2016, 9:48:32 AM6/21/16
to golan...@googlegroups.com
 
On Tue, Jun 21, 2016, at 01:45 PM, andrew...@gmail.com wrote:
>> I am not saying that generics is bad, but I am questioning whether generics is necessary.
 
Please, do not panic.
If you worry about the following things:
- Generated code will grow when used generics
- Generated code will be more complex when used generics
- Execution performance will slow down when used generics
- Memory consumption will grow when used generics
 
I don't believe most people in this mailing list are worried about those things. They are worried about things like code complexity, readability and increase in cognitive load to decipher chains of type definitions.
 
Ian

andrew...@gmail.com

unread,
Jun 21, 2016, 10:18:04 AM6/21/16
to golang-nuts
>> increase in cognitive load to decipher chains of type definitions.

Sorry, but who are members of this mail lists?
This is a first time when I hear about such loads such as the `cognitive load`.
Also I am possible here a single person who does not know anything about the `cognitive load to decipher chains of type definitions`.
I am mostly business man and a programmer just for my own requirements.
I love to implements parsers, code generators, some effective algorithms for solving some problem at the most effective time.
Also my work includes a diagnose the failures and locate them.
And I want to apologize for my illiteracy (thick-headed), but I do not really was expecting that all members of this mail list worry mostly about the ` increase in cognitive load to decipher chains of type definitions` and don't worry about other things.
I am sorry.

Ian Davis

unread,
Jun 21, 2016, 10:24:23 AM6/21/16
to golan...@googlegroups.com
What I mean is that most people who have followed the past generics discussions already know it's possible to implement them with efficient memory use and performance. So they don't need to worry much about that.
 
What stops them being added to the language is: can they be implemented without making Go a much more complex language than it is now.  In my opinion complex or large languages result in code that is much harder and expensive to maintain over the long term.
 
Ian
 

Henry

unread,
Jun 21, 2016, 10:29:37 AM6/21/16
to golang-nuts
You still haven't provided any argument why generics is indispensable.

The reason why I am no longer sure about my position in this issue is because -while I agree that generics is useful- I don't think that generics is essential. In fact, all of C++ features are useful and implemented in a very efficient manner, but take a look what happened when you slab that many features together. If you can do away with less, I think you should go for less. The trouble is deprecating language features is a lot harder than deprecating APIs in the standard library, while programming fads come and go.

andrew...@gmail.com

unread,
Jun 21, 2016, 11:06:30 AM6/21/16
to golang-nuts
>> What I mean is that most people who have followed the past generics discussions already know it's possible to implement them with efficient memory use and performance. So they don't need to worry much about that. 

Thank you for explanation.

>> can they be implemented without making Go a much more complex language than it is now

What is a measure of the syntax complexity in the Go language.
What this means and how they should be measured such a relative criteria?
- Much more complex
- More complex
- Slightly more complex
- Little bit more complex

Also how you personally measure these cognitive loads?

1.Sample (pattern)

type KeyValuePair interafce {
  key interface{}
  val  interface{}
}

2.Sample (pattern)

type KeyValuePair<K, V> interafce {
  key K
  val  V
}

What about that a value of the #2 sample is more complex than #1 sample on the 146%?
With permissible error of no more than 5%.

I think that possible we should to start a discussion about that problem (complexity measurement of the Go language perception).

I'm not kidding.

Egon

unread,
Jun 21, 2016, 11:50:33 AM6/21/16
to golang-nuts, andrew...@gmail.com
On Tuesday, 21 June 2016 17:18:04 UTC+3, andrew...@gmail.com wrote:
>> increase in cognitive load to decipher chains of type definitions.

Sorry, but who are members of this mail lists?
This is a first time when I hear about such loads such as the `cognitive load`.


Also I am possible here a single person who does not know anything about the `cognitive load to decipher chains of type definitions`.

See http://www.boost.org/, although it is at the very far end of the spectrum (for example Dijkstra algorithm). Or, examples in "Modern C++ Design Generic Programming".

Or if I take the trivial example and write some facilitated code (I know that concurrent map API wouldn't look like that):

package conc

type Map<K,V> struct {
  Entries []Entry<K,V>
}
type Entry<K, V> struct {
  Key K
  Value V
}

package main

func PrintItems<T>(m conc.Map<Person, T>, format func(T)string) {
  var entry conc.Entry<Person, T>
  for m.Next(&entry) {
    fmt.Println(entry.Key.Name, format(entry.Value))
  }
}

func main() {
  m := conc.NewMap<Person, string>()
  PrintItems<string>(m, func(x string) string { return x })
}

And I'm not doing anything complicated, yet. This approach doesn't properly isolate the generic parts and you get code that is completely mixed with it.

I am mostly business man and a programmer just for my own requirements.
I love to implements parsers, code generators, some effective algorithms for solving some problem at the most effective time.
Also my work includes a diagnose the failures and locate them.
And I want to apologize for my illiteracy (thick-headed), but I do not really was expecting that all members of this mail list worry mostly about the ` increase in cognitive load to decipher chains of type definitions` and don't worry about other things.
I am sorry.

xingtao zhao

unread,
Jun 21, 2016, 2:11:40 PM6/21/16
to golang-nuts, parais...@gmail.com, andrew...@gmail.com
For example:

type Int int
func (i Int) Add(j Int) Int { return i + j }

type Number<T Number<T>> interface {
    Add(j T) T
}

If we want Int satisfy the interface Number<Int>, we have to create two versions of function Int.Add, one for concrete type Int, one with generic type T

And if we want to support:

var i Int
var v interface{} = i
var n Number<Int> = v.(Number<Int>)

We will have to create a stub for Int.Add in itable of Number, and dynamically adapt the parameter types at runtime.

Axel Wagner

unread,
Jun 21, 2016, 2:56:01 PM6/21/16
to andrew...@gmail.com, golang-nuts
The issue is, that a "KeyValuePair<K, V>" (no matter if you implemented it via generics or like you mention via interfaces) is a fundamentally useless type and generics encourage people to add useless types. A "KeyValuePair<K, V>" is a "struct { Key K, Value V }", plain and simple. It's not an interface and it's not a generic type, it's simply a struct.

I agree, that generics are useful, but they seem to be mainly useful for people who write "frameworks", not so much for people who write "applications" and so far, go is pretty much a language for the latter. Whenever people try to justify generics, they do it from a "I want to write a framework" POV - and whenever I miss generics, I do because I'm currently writing a framework of some kind.

Generics empower abstractions, but most programmers are very bad at building abstractions, so if you make it easy to build abstractions, you will end up with a lot of bad abstractions (have a look at java). So, to a certain degree, the value of go is, to *not* make building abstractions overly simple. That leads to abstractions being used only where they are essential and being left out where they are superfluous. This is where reduced cognitive overhead comes into play - limiting the levels of abstractions that people need to deal with to the bare essentials. Java is bloated and hard to use, not because the language is bad, but because it has a history of programmers building bad abstractions into it which gets stacked on top of each other. So, yes, if you compare a bad abstraction using interfaces with a bad abstraction using generics, generics will, in general, compare very well. But you just shouldn't build the bad abstraction in the first place.

The second concern with complexity is the spec. The exact behavior and semantics of generics need to be spec'ed and useful generics need a lot specification. For example, the current rules for type inference can be understood completely just by looking at a single expression and it's type and it's correspondingly simple to implement and spec. Generics usually need more powerful type inference methods to not be cumbersome, which will take up a lot of space in the spec. As humans, just like computers, have very little memory, the time it takes to understand the spec will grow superlinear with the length of it, due to frequent cache misses, so a long spec will significantly increase the time needed to learn the language. In the same vein, to understand a language, you need to know about interactions between it's different concepts, not just the concepts itself, so the needed space and time complexity to learn a language also grows quadratic in the number of concepts in the language (in general). All of that contributes to why people are wary of adding new concepts to go - the costs in terms of understanding and learning the language are huge and they grow very much superlinear in the number of concepts added, so each added concept must be carefully examined (I know go for years and I still learn new things about interactions between different concepts all the time).


I sometimes miss generics, yes, but I also believe adding them will make the language significantly harder to learn and will significantly worsen the quality of go code in the wild, so it would likely eliminate the reasons I like go currently (which is that go code is usually of exceptionally high quality, uniform and easy to understand).

--

xingtao zhao

unread,
Jun 21, 2016, 3:38:16 PM6/21/16
to golang-nuts, andrew...@gmail.com
I totally agree that I always want generics mainly when I am writing a framework. But I do not agree with you on that "go is pretty much a language for writing applications". I don't think go is powerful enough that we do no need any framework. I don't think we can always develop applications from the scratch. And I do think it is a big help for go if we have a lot of frameworks/libraries right on your hand. I do think it is a waste of engineer time to develop all the tools from scratch. Currently there are many frameworks/libraries have to use a lot of reflections just because generic is missing. This both makes the framework/library hard to use, slower, and less safe (a lot of runtime type checking which should/could be done at compiling time).

Think about how many people were asking: For the following types, how can I convert []Fool to []Barer, and via verse?

type Barer interface { Bar() }
type Foo struct{ ... }
func (f *Foo) Bar() { ... }
func Process([]Barer) { ... }

We have to tell them write a helper function to convert it. While in fact, if we have generic, and declare Process as:

func Process<T Barer>([]T) { ... } 

we do not need this kind of conversions at all. Interface is perfect for abstraction of the direct layer, while it is not good enough for abstractions inside of slice, map, chan, and structure, especially for slices which are used a lot in go code. I think this is a very big requirement by the users who is writing "applications" as well.

Axel Wagner

unread,
Jun 21, 2016, 4:35:30 PM6/21/16
to xingtao zhao, golang-nuts, andrew...@gmail.com
On Tue, Jun 21, 2016 at 9:38 PM, xingtao zhao <zhaox...@gmail.com> wrote:
I totally agree that I always want generics mainly when I am writing a framework. But I do not agree with you on that "go is pretty much a language for writing applications". I don't think go is powerful enough that we do no need any framework. I don't think we can always develop applications from the scratch.

I agree. But I don't think that "reuse" implies "framework", lest "requires generic". For example, you can write very easily reusable components based on net/http. Much, much more so, than what we probably would have with generics. For reuse you need to build abstractions, but the limited way in which we can abstract in go requires you to think hard about *good* ones.
 
And I do think it is a big help for go if we have a lot of frameworks/libraries right on your hand. I do think it is a waste of engineer time to develop all the tools from scratch. Currently there are many frameworks/libraries have to use a lot of reflections just because generic is missing.

No, they don't have to, they want to, because thinking about good abstractions is hard and reflection is the only way go gives you to write bad and ultimately useless abstractions.
 
This both makes the framework/library hard to use, slower, and less safe (a lot of runtime type checking which should/could be done at compiling time).

I disagree that safety for it's own sake is a useful goal. I consider type safety like I consider memory safety: You definitely want the vast majority of your code to be memory safe, but sometimes you have to use unsafe in limited cases and that's fine and it doesn't compromise the safety of the program as a whole. If the unsafe amount of code is small enough, it's easily verified by hand. The same goes for type-safety. It is fine to lose some type-safety in limited amounts of code, as long as you are careful, review it and make the limitations simple to understand.

A good example are the encoding packages - that's a decent place to use reflection, even though it limits your static type safety. But there is limited API surface that you need to understand, usually just two functions or so. It's easy to still write safe programs that way.
 
Think about how many people were asking: For the following types, how can I convert []Fool to []Barer, and via verse?

type Barer interface { Bar() }
type Foo struct{ ... }
func (f *Foo) Bar() { ... }
func Process([]Barer) { ... }

We have to tell them write a helper function to convert it.

No, we don't. We have to write a loop. We might opt to write a helper if we need to do that very often. But, honestly, we almost never do.
 
While in fact, if we have generic, and declare Process as:

func Process<T Barer>([]T) { ... } 

we do not need this kind of conversions at all. Interface is perfect for abstraction of the direct layer, while it is not good enough for abstractions inside of slice, map, chan, and structure, especially for slices which are used a lot in go code. I think this is a very big requirement by the users who is writing "applications" as well.

I disagree. You are, again, writing frameworks. A perfectly understandable and readable loop that turns a []Foo into a []Barer is four lines of code. A helper function is six. You probably need to do that on average at most .1 times per application that you write. I don't consider .6 lines of code per application saved a significant saving that could serve as an argument here.

And you are also misrepresenting, what would happen. Because realistically (look at existing languages with generics) the signature of that function would more be something like

func Map<Iterable<T>, O>(in T, f func(T) O) Iterable<O>

Just to save .6 lines of code per application.

Alex Bligh

unread,
Jun 21, 2016, 6:07:09 PM6/21/16
to Axel Wagner, Alex Bligh, andrew...@gmail.com, golang-nuts

On 21 Jun 2016, at 19:55, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:

> The issue is, that a "KeyValuePair<K, V>" (no matter if you implemented it via generics or like you mention via interfaces) is a fundamentally useless type and generics encourage people to add useless types. A "KeyValuePair<K, V>" is a "struct { Key K, Value V }", plain and simple. It's not an interface and it's not a generic type, it's simply a struct.
>
> I agree, that generics are useful, but they seem to be mainly useful for people who write "frameworks", not so much for people who write "applications" and so far, go is pretty much a language for the latter. Whenever people try to justify generics, they do it from a "I want to write a framework" POV - and whenever I miss generics, I do because I'm currently writing a framework of some kind.

Not sure that's fair.

For background, I'd quite like generics and liked Ian's last proposal quite a bit, but I also appreciate the cleanliness of go.

An recent example, in an real life application, where generics would have been useful is as follows.

I had a cache type thing built around a red-black tree, or more precisely two red black trees. The items were structs, and I needed one RB tree to be ordered by time of insertion (then key, for uniqueness), and one by key. One red-black tree dealt with expiries, and one with finding the item. In the absence of generics, I needed to embed the struct in a 'ByTime' and 'ByItem' struct, each of which implement a different 'LessThan' method. And everything that accessed the trees had a bunch of ugly casting from an interface{} to the relevant type. This does not make for readable code in any form. A RB tree that was implemented as a generic (and perhaps took a 'LessThan' function as a lambda) would have been far far cleaner in *my* code.

So saying it's all about being useful for people who write "frameworks" is unfair. It would actually have made the application code (i.e. the library user) far easier to read.

Of course it's possible to generate foolish uses (I would however note in passing that even with KeyValuePair things might not be as simple as you think, as it would (presumably) require comparability of K type items); however, it's also possible to perform stupidity with existing go syntax. The question is whether generics encourage it.

--
Alex Bligh




ad...@gardener.com

unread,
Jun 21, 2016, 7:20:49 PM6/21/16
to golang-nuts, andrew...@gmail.com


On Tuesday, June 21, 2016 at 2:56:01 PM UTC-4, Axel Wagner wrote:
The issue is, that a "KeyValuePair<K, V>" (no matter if you implemented it via generics or like you mention via interfaces) is a fundamentally useless type and generics encourage people to add useless types. A "KeyValuePair<K, V>" is a "struct { Key K, Value V }", plain and simple. It's not an interface and it's not a generic type, it's simply a struct.

I agree, that generics are useful, but they seem to be mainly useful for people who write "frameworks", not so much for people who write "applications" and so far, go is pretty much a language for the latter. Whenever people try to justify generics, they do it from a "I want to write a framework" POV - and whenever I miss generics, I do because I'm currently writing a framework of some kind.

generics would greatly reduce the work required to reproduce or possibly eliminate all work when building container types.

 
Generics empower abstractions, but most programmers are very bad at building abstractions, so if you make it easy to build abstractions, you will end up with a lot of bad abstractions (have a look at java). So, to a certain degree, the value of go is, to *not* make building abstractions overly simple. That leads to abstractions being used only where they are essential and being left out where they are superfluous. This is where reduced cognitive overhead comes into play - limiting the levels of abstractions that people need to deal with to the bare essentials. Java is bloated and hard to use, not because the language is bad, but because it has a history of programmers building bad abstractions into it which gets stacked on top of each other. So, yes, if you compare a bad abstraction using interfaces with a bad abstraction using generics, generics will, in general, compare very well. But you just shouldn't build the bad abstraction in the first place.
 
this is true with almost every feature of the language, so its absurd to even use this as an argument. generics would greatly reduced computational complexity.
 

The second concern with complexity is the spec. The exact behavior and semantics of generics need to be spec'ed and useful generics need a lot specification. For example, the current rules for type inference can be understood completely just by looking at a single expression and it's type and it's correspondingly simple to implement and spec. Generics usually need more powerful type inference methods to not be cumbersome, which will take up a lot of space in the spec. As humans, just like computers, have very little memory, the time it takes to understand the spec will grow superlinear with the length of it, due to frequent cache misses, so a long spec will significantly increase the time needed to learn the language. In the same vein, to understand a language, you need to know about interactions between it's different concepts, not just the concepts itself, so the needed space and time complexity to learn a language also grows quadratic in the number of concepts in the language (in general). All of that contributes to why people are wary of adding new concepts to go - the costs in terms of understanding and learning the language are huge and they grow very much superlinear in the number of concepts added, so each added concept must be carefully examined (I know go for years and I still learn new things about interactions between different concepts all the time).

this comment is more mumbo-jumbo
 


I sometimes miss generics, yes, but I also believe adding them will make the language significantly harder to learn and will significantly worsen the quality of go code in the wild, so it would likely eliminate the reasons I like go currently (which is that go code is usually of exceptionally high quality, uniform and easy to understand).

another absurd comment. generics would greater benefits and reduce the complexity of code.

Marcus Yip

unread,
Jun 21, 2016, 7:38:05 PM6/21/16
to Egon, Axel Wagner, Rodrigo Kochenburger, Ian Lance Taylor, parais...@gmail.com, Ian Davis, Henry, Micky, Alex Bligh, xingtao zhao, 'Axel Wagner' via golang-nuts, andrew...@gmail.com, ad...@gardener.com
On Tuesday, June 21, 2016 at 2:56:01 PM UTC-4, Axel Wagner wrote: The issue is, that a "KeyValuePair" (no matter if you implemented it via generics or like you mention via interfaces) i

ad...@gardener.com

unread,
Jun 21, 2016, 7:44:56 PM6/21/16
to golang-nuts
generics would greatly improve code reuse for many algorithms- this one fact alone trumps all opposition. In fact, go's builtin container types have generics-like functionality already. Imagine arrays, slices, maps without this functionality- exactly, enough said. the arguments against generics are mind-numbing.

with that said, the best approach for those seeking generics in go is to produce preprocessers chained with the existing go tools, and wrapping that chain in a new toolset. this approach would reduce the effort needed to produce and maintain this new toolset. Most of the useful generic-like functionality is easily produced at compile time. I doubt there would be any benefit pushing generics into the language proper.

I'd also like to think generics could be introduced very similar to the how unsafe is designed- as a compiler feature.

Matt Ho

unread,
Jun 21, 2016, 10:12:09 PM6/21/16
to golang-nuts, ad...@gardener.com
I think I have to agree with most of the posters here about generics.  In theory, I miss them.  However, in practice, I find that there are usually only a few times during the course of a project where I wish I had generics.  And then after writing a few lines of code to do what the generic would have done, I find I stop missing them.  

M



andrew...@gmail.com

unread,
Jun 22, 2016, 12:21:49 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> The issue is, that a "KeyValuePair<K, V>" (no matter if you implemented it via generics or like you mention via interfaces) is a fundamentally useless type and generics encourage people to add useless types. A "KeyValuePair<K, V>" is a "struct { Key K, Value V }", plain and simple. It's not an interface and it's not a generic type, it's simply a struct.

I not agree with you only because with generics this pattern is always type safe.

Eg.

type Foo<K, V> struct {
  key K
  val V
}

func accepInt(i int) {
}

func some() string {
  foo := &Foo<string, int>()
  // some code

  // Compile error: `key` is not `int`
  acceptInt(foo.key)

  // Compile error: `key` (int) cannot be compared with `string`
  if foo.key == foo.val {
  }

  // Compile error: `key` is not `string`
  return foo.val
}

func baz(foo Foo<K, V>) bool {
  // Compile error: `key` (K) cannot be compared with `V`  
  return foo.key == foo.val
}

Axel Wagner

unread,
Jun 22, 2016, 3:03:23 AM6/22/16
to Andrew Mezoni, golang-nuts
I don't know what you are doing, here, but…

func some() string {
    foo := struct{ Key string, Value int }{}
    // some code

    // compie error: Can't use foo.Key (type string) as int
    acceptInt(foo.Key)

    // compile error: mismatched types string and int
    if foo.Key == foo.Value {
    }

    // compile error: Can't use foo.Value (type int) as string
    return foo.Value
}

perfectly type safe.

andrew...@gmail.com

unread,
Jun 22, 2016, 3:37:08 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> perfectly type safe.

Perfectly type safe but not perfectly reusable.

What If we slightly complicate the task?

Now is my code and I want to see on your code exampe (perfectly type safe but and perfectly reusable)

type Foo<K, V> interface {
  Get(key K) V
  Set(key K, val V)
}

func foo() (int, Foo<string, int>) {
  foo := &Foo<string, int>{}
  // ...
  foos := []Foo<K,V>{}
  // ...
  k := "London"
  // ...
  return 55, foo2(foos, 55, k)
}

// This code is type safe and reusable
foo2<K, V> (foos []Foo<K,V>, i int, key K) (V, Foo<K,V>) {
  foo := foos[i]
  return foo.Get(key), foo
}

Axel Wagner

unread,
Jun 22, 2016, 3:48:10 AM6/22/16
to Andrew Mezoni, golang-nuts
And my point is and was: It doesn't *need* to be both type safe *and* reusable, unless you are focusing your energy on writing frameworks or treating type safety as a goal solely for it's own sake. It's perfectly fine to write small stuff yourself (for example like this) with explicit types and it's perfectly fine to locally not have strict compiler checks if you are doing something complicated that needs to be reusable. If you focus on application writers, instead of the authors of the frameworks they might use, you'll recognize that either choice just isn't that bad.

--

andrew...@gmail.com

unread,
Jun 22, 2016, 3:49:06 AM6/22/16
to golang-nuts, andrew...@gmail.com
Sorry for typo and possible spam but correct example here:

type Foo<K, V> interface {
  Get(key K) V
  Set(key K, val V)
}

func foo() (int, Foo<string, int>) {
  // Typo: foo := &Foo<string, int>{}
  foo := &fooStruct<string, int>{}
  

Egon

unread,
Jun 22, 2016, 4:03:37 AM6/22/16
to golang-nuts, andrew...@gmail.com
As a general suggestion:

Do not use fabricated examples, use concrete real-world examples.

Fabricated examples make harder to examine the practical side of generics -- i.e. how would you solve a particular problem with/without generics. The fabricated examples can suggest a problem where there is none, alternatively they can trivialize the actual problem without solving it in the first place. And by concrete real-world examples, I really mean code that someone would pay you to write.

Talking about reusability of faciliated code is nonsense, because that code isn't usable in the first place.

andrew...@gmail.com

unread,
Jun 22, 2016, 4:12:10 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> And my point is and was: It doesn't *need* to be both type safe *and* reusable, unless you are focusing your energy on writing frameworks or treating type safety as a goal solely for it's own sake.

It does NEED to be BOTH type safe and reusable.

1. Type safety prevent the programmers to make errors.
2. Reusabilty prevent the programmers to implement the same functionality again and again and again and again and again and again...

Code reuse, also called software reuse, is the use of existing software, or software knowledge, to build new software, following the reusability principles.

Reusability

In computer science and software engineeringreusability is the use of existing assets in some form within the software product development process. 

Candidate design features for software reuse include:

P.S.

We continue the discussion about the dangers of `to be both type safe *and* reusable`?

Me and most of us MUST focusing our energy on writing high quailty and reusable software.
If someone doesn't agree I will not retort.
Want write software only for himself? Not a problem, your energy are your energy.

Axel Wagner

unread,
Jun 22, 2016, 5:05:19 AM6/22/16
to Andrew Mezoni, golang-nuts
You are not bringing anything new to the table here, except the attempt to insult my intelligence, apparently.

There are huge software projects out there written in languages that are less type safe than go and the vast majority of code written is not reusable. Both also aren't absolutes. There is no binary "type safe or not type safe" and no "reusable or not reusable". There are only more or less expressive type systems and there are only pieces of code that are easier or harder to reuse. Every language finds their own tradeoff for them and every programmer must find their own preferences for the tradeoffs they require. go is explicitly designed as an engineering language and the defining characteristic of engineering is the acknowledgment of tradeoffs. So, without acknowledging that this needs to be a tradeoff, there really is not much point in talking about how to improve go. If someone prefers the extreme, might I suggest agda instead of go? I don't know of a more type safe language out there.

On Wed, Jun 22, 2016 at 10:11 AM, <andrew...@gmail.com> wrote:
We continue the discussion about the dangers of `to be both type safe *and* reusable`?

That is not what I was saying. I was talking about the dangers of generics for producing clear and readable code. That is not an issue with being both, it's an issue with generics. "It needs to be both" just isn't a very good argument in favor of them.
 
Me and most of us MUST focusing our energy on writing high quailty and reusable software.

Instead, we seem to focus your energy on debating whether we need to add generics to go or not. There are people out there writing high quality and reusable software *right now*, both in go and not in go. Coincidentally, that's what I'm going to do now too.
 
Want write software only for himself? Not a problem, your energy are your energy.

I get paid to write and maintain large software in large teams, in many languages falling into very different spots on the scale of type safety and re-usability and my opinions are informed by that. You shouldn't presume that people disagree with you because they know less.

andrew...@gmail.com

unread,
Jun 22, 2016, 6:02:20 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> You are not bringing anything new to the table here, except the attempt to insult my intelligence, apparently.

I do not have any claims to anyone personally.
I only defend my own point of view.

P.S.

Also I don't love to see or use some (not my own) code which written like a mess.
If I cannot understand some code then this does not means that this code is bad.

But if I know that a some code has been written carelessly and can be written more diligently then it gives me a negative reaction on that work (if there was no reason to write it carelessly).

That is, write code once and and use it everywhere.
Or more precisely, write (high quality and reusable) code once and and use it everywhere (and give the possibility to use it to everyone).

The generics programming are very good suit for that.

In the simplest definition, generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters

The term generic programming describes a programming paradigm whereby fundamental requirements on types are abstracted from across concrete examples of algorithms and data structures and formalised as concepts, with generic functions implemented in terms of these concepts, typically using language genericity mechanisms as described above.

P.S.

I am sorry but here we discuss mostly about the advantages and disadvantages of the generic programming (of course, in Go language).
Here we do not discuss: How to coding in Go language without generic programming.
I think that it should be different topic with appropriate title.

Egon

unread,
Jun 22, 2016, 6:28:07 AM6/22/16
to golang-nuts, andrew...@gmail.com
Are there any points missing in the summary? 

This thread was a about a specific proposal, but as usual with threads they diverge from the original topic.
 
Here we do not discuss: How to coding in Go language without generic programming.
I think that it should be different topic with appropriate title.

The version without generics is necessary to compare how well a particular generics approach improves the current language and code.

Andrew Mezoni

unread,
Jun 22, 2016, 8:32:53 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> The version without generics is necessary to compare how well a particular generics approach improves the current language and code.

I am sorry but this is obviously that any useful feature (in the context of solving problems) improves the current language and code.
Another question: how new (version of the) language will look cosmetically with this newly added feature.

I found that the second question here more important then the first.

It is obviously correct that the code with a generics (in appropriate place) would be more useful then other code.
But here I undestand one interesting thing which states: Some obviously useful feature can significantly spoil the overall picture of language.

For me this is very strange only because even a C language also has been improved after when it initially was introduced.

In the years following the publication of K&R C, several features were added to the language, supported by compilers from AT&T and some other vendors. These included:


C99 introduced several new features, including inline functions, several new data types (including long long int and a complex type to represent complex numbers), variable-length arrays and flexible array members, improved support for IEEE 754 floating point, support for variadic macros (macros of variable arity), and support for one-line comments beginning with //

The C11 standard adds numerous new features to C and the library, including type generic macros, anonymous structures, improved Unicode support, atomic operations, multi-threading, and bounds-checked functions. 

P.S.

I guess I should think that this is very bad, because my first acquaintance with the language began with the ZX-Spetrum and continued with the Borland Turbo C.

Indeed, the C language is now looks much worse cosmetically than before. 
But should I regret it?
Maybe...

Viktor Kojouharov

unread,
Jun 22, 2016, 8:45:01 AM6/22/16
to golang-nuts
https://golang.org/pkg/math/ and https://golang.org/pkg/container/ are just two stdlib packages that would greatly benefit from some kind of generics. I'm pretty sure there are more packages in the stdlib that would be greatly improved. And that's just the standard library. 

Viktor Kojouharov

unread,
Jun 22, 2016, 8:55:24 AM6/22/16
to golang-nuts, andrew...@gmail.com


On Tuesday, June 21, 2016 at 9:56:01 PM UTC+3, Axel Wagner wrote:
The issue is, that a "KeyValuePair<K, V>" (no matter if you implemented it via generics or like you mention via interfaces) is a fundamentally useless type and generics encourage people to add useless types. A "KeyValuePair<K, V>" is a "struct { Key K, Value V }", plain and simple. It's not an interface and it's not a generic type, it's simply a struct.

And yet the 'K' and 'V' within the struct are generic types. A struct is never simple. That's its whole point.
 

I agree, that generics are useful, but they seem to be mainly useful for people who write "frameworks", not so much for people who write "applications" and so far, go is pretty much a language for the latter. Whenever people try to justify generics, they do it from a "I want to write a framework" POV - and whenever I miss generics, I do because I'm currently writing a framework of some kind.

A lot of these 'applications' that you think are the language's main target are built on top of libraries, including stdlib, written by that same language. And these same libraries can benefit from having generics, once again, including stdlib.
 

Generics empower abstractions, but most programmers are very bad at building abstractions, so if you make it easy to build abstractions, you will end up with a lot of bad abstractions (have a look at java). So, to a certain degree, the value of go is, to *not* make building abstractions overly simple. That leads to abstractions being used only where they are essential and being left out where they are superfluous. This is where reduced cognitive overhead comes into play - limiting the levels of abstractions that people need to deal with to the bare essentials. Java is bloated and hard to use, not because the language is bad, but because it has a history of programmers building bad abstractions into it which gets stacked on top of each other. So, yes, if you compare a bad abstraction using interfaces with a bad abstraction using generics, generics will, in general, compare very well. But you just shouldn't build the bad abstraction in the first place.

I've found this type of thinking quite poisonous. The "lets bereave ourselves of something universally lauded as useful, because it _might_ be used in the wrong manner by some people" mentality is just wrong and prevents any kind of useful evolution under any context. 
 

The second concern with complexity is the spec. The exact behavior and semantics of generics need to be spec'ed and useful generics need a lot specification. For example, the current rules for type inference can be understood completely just by looking at a single expression and it's type and it's correspondingly simple to implement and spec. Generics usually need more powerful type inference methods to not be cumbersome, which will take up a lot of space in the spec. As humans, just like computers, have very little memory, the time it takes to understand the spec will grow superlinear with the length of it, due to frequent cache misses, so a long spec will significantly increase the time needed to learn the language. In the same vein, to understand a language, you need to know about interactions between it's different concepts, not just the concepts itself, so the needed space and time complexity to learn a language also grows quadratic in the number of concepts in the language (in general). All of that contributes to why people are wary of adding new concepts to go - the costs in terms of understanding and learning the language are huge and they grow very much superlinear in the number of concepts added, so each added concept must be carefully examined (I know go for years and I still learn new things about interactions between different concepts all the time).

And yet this hasn't stopped people from successfully learning and utilizing languages that have generics are part of their specifications. The fact still remains that unlike a lot of other proposed languages additions that are immediately dropped, generics are still open for discussion by the language developers themselves. 


I sometimes miss generics, yes, but I also believe adding them will make the language significantly harder to learn and will significantly worsen the quality of go code in the wild, so it would likely eliminate the reasons I like go currently (which is that go code is usually of exceptionally high quality, uniform and easy to understand).

I'm sorry, but that's just fearmongering. Unless you have some kind of magic crystal ball, there's now way you'd know how the quality of go code will change. You don't even know what the implementation will look like ...

Egon

unread,
Jun 22, 2016, 9:25:52 AM6/22/16
to golang-nuts, andrew...@gmail.com
On Wednesday, 22 June 2016 15:32:53 UTC+3, Andrew Mezoni wrote:
>> The version without generics is necessary to compare how well a particular generics approach improves the current language and code.

I am sorry but this is obviously that any useful feature (in the context of solving problems) improves the current language and code.

Yes.. Of course, this presupposes that the particular generics approach is useful.
How would you demonstrate that one generics approach is more useful than another?

Another question: how new (version of the) language will look cosmetically with this newly added feature.

I found that the second question here more important then the first.

It is obviously correct that the code with a generics (in appropriate place) would be more useful then other code.

Which generics approach?
Which places need that kind of generics?
Should the language solve all generics related problems or only some?
Are there better solutions to those problems than generics?
Would some meta-programming approach negate the need for generics?

Also, is "useful in some particular case" the best metric to decide whether something should be included in the language?

e.g. #define max(a,b) ((a) < (b) ? (b) : (a))

But here I undestand one interesting thing which states: Some obviously useful feature can significantly spoil the overall picture of language.

For me this is very strange only because even a C language also has been improved after when it initially was introduced. 

Have you also looked at the proposals that were rejected? If someone made the proposal, it must have been useful to someone.

In the years following the publication of K&R C, several features were added to the language, supported by compilers from AT&T and some other vendors. These included:
Were there any competing proposal for those same feature sets? Why were the others rejected whereas these particular ones accepted?

Also, what if, instead functions just allowing returning struct/unions, it would have also allowed to return multiple parameters? Would that have resulted better or worse C code?

Sean Russell

unread,
Jun 22, 2016, 9:28:01 AM6/22/16
to golang-nuts
On Tuesday, June 21, 2016 at 10:29:37 AM UTC-4, Henry wrote:
> You still haven't provided any argument why generics is indispensable.

That can't be the litmus for language feature inclusion; if it was, Go would resemble ASM.

In my personal experience, something North of 50% of my non-trivial applications could have been more simple with some form of generics allowing me to reduce code duplication. In particular, any application dealing with demarshaling of data from a large set of similar functions (e.g. web calls) are good examples. Have 30 functions to make the same web calls and perform the same demarshaling calls -- especially where the web call may be more complex, as in a SOAP call -- does not make the code cleaner, easier to cognitively parse, or more safe. Indeed, in almost every such case, generics reduce code duplication and make the code safer from bugs, and easier to maintain.

Indispensable? That's subjective, and very few language features satisfy that requirement. For me, the strongest argument against it is that if the core Go team can't think of a way to implement it cleanly and efficiently, then I trust it's a hard problem. I'm sure they've looked at it; it must be the single most commonly requested, hashed-over, and proposal-backed feature.

--- SER

Andrew Mezoni

unread,
Jun 22, 2016, 9:29:44 AM6/22/16
to golang-nuts, andrew...@gmail.com
>> The fact still remains that unlike a lot of other proposed languages additions that are immediately dropped, generics are still open for discussion by the language developers themselves. 

I think that the problerm only in the Go developers brcause them even does try to implement them experimentally.
That is so called "hidden" implementation when the runtime engine supports the feature but compiler does not.
That is, available for testing only through the reflection but without grammar specification on that how to declare and use time at compile time.
As for me, they should have them (support of the generic types) in the runtime engine and in the reflection for the testing purpose but does not introduce them until they not decide that them are ready for real use.

Axel Wagner

unread,
Jun 22, 2016, 9:50:02 AM6/22/16
to Viktor Kojouharov, golang-nuts, Andrew Mezoni
On Wed, Jun 22, 2016 at 2:55 PM, Viktor Kojouharov <vkojo...@gmail.com> wrote:
And yet the 'K' and 'V' within the struct are generic types. A struct is never simple. That's its whole point.

I don't know what you mean by that. A struct is very simple, it's flat data.
 
A lot of these 'applications' that you think are the language's main target are built on top of libraries, including stdlib, written by that same language. And these same libraries can benefit from having generics, once again, including stdlib.

I agree. But I also think they can suffer from them. And I don't find it at all obvious which effect will dominate. From my experience with other languages, the overall effect will probably be negative, because the number of places where they help is very small, whereas the number of places where they hurt and are still being used (judging, again, from other languages) is very large.
 
I've found this type of thinking quite poisonous. The "lets bereave ourselves of something universally lauded as useful, because it _might_ be used in the wrong manner by some people" mentality is just wrong and prevents any kind of useful evolution under any context. 

Again, I am judging here from my experiences with other languages that do have generics and I am also taking into account the arguments brought forth by people proposing them for go. There is strong evidence that the correct phrasing is "will", not "might".

And yet this hasn't stopped people from successfully learning and utilizing languages that have generics are part of their specifications.

Yes, but I haven't claimed impossibility, but hardship. Let's look at it from this way: If generics are as indispensable as claimed here, why are people still using go then? For years the adoption of go has grown, not dropped, especially for large-scale applications and despite a lack of generics which is painted as a necessity. That just doesn't make any sense. I'm claiming that the reason go is still being used is *because* it's lack of generics (and other features), rather than despite of it. It's because it's a comparatively simple and easily understandable language.
 
The fact still remains that unlike a lot of other proposed languages additions that are immediately dropped, generics are still open for discussion by the language developers themselves. 

Yes. And I would treat proposals by the go team very differently from the random repeated proposals to golang-nuts by people I have never heard of. Because they've come up with the language as I like it and I trust them to not add a proposal that goes contrary to it's spirit.
 
I'm sorry, but that's just fearmongering. Unless you have some kind of magic crystal ball, there's now way you'd know how the quality of go code will change.  You don't even know what the implementation will look like ...

As mentioned above, I'm working from my experience with languages that currently have them and from examples people have put forward to justify their need. As most if not all "wild" proposals so far have argued from a standpoint of existing implementations and tried to apply existing implementations to go, I consider that very fair.

Thomas Bushnell, BSG

unread,
Jun 22, 2016, 9:10:34 PM6/22/16
to Viktor Kojouharov, golang-nuts
Really? How would you implement math.Max with generics? 

Thomas

--

Michael Jones

unread,
Jun 22, 2016, 9:31:20 PM6/22/16
to Thomas Bushnell, BSG, Viktor Kojouharov, golang-nuts
The wonderful history of symbolic algebra at IBM (Scratchpad, Scratchpad II) resulted in a third system, Axiom, that has always seemed to me a bright lesson in types. It boldly goes into the topic with rigor, using category theory to decide what relationships make sense and what the nature of a tensor cross product of types truly is. 

A key part of that is the notion of more general and more specific types. This makes it possible to define a function that needs arguments supporting a less than order relationship so it could be applied to integer and real arguments, but not to complex numbers. This is just the mechanism that makes a generic Max() easy to create. Imagine a definition like:

func {T type, where a,b ∈T allows a<b} Max(a, b T) T {
if a < b {
    return b
}
return a
}

This is a nice way to answer the riddle and it is just what Axiom supports (but expresses differently).

Michael Jones

Thomas Bushnell, BSG

unread,
Jun 22, 2016, 9:32:47 PM6/22/16
to Michael Jones, Viktor Kojouharov, golang-nuts

But Michael, that implementation isn't right for floats! Check out the actual source for math.Max.

Michael Jones

unread,
Jun 22, 2016, 11:07:39 PM6/22/16
to Thomas Bushnell, BSG, Viktor Kojouharov, golang-nuts

Yes, for floats with NANs and the like, you must specialize. My replacement for the standard library sort (3x to 10x faster in the common case) had to have more macro generators than expected just to handle the "uncomparables" in float32 and float64.

You are right about that issue.

P.S. By the way, Hi! Good to hear from you.

Thomas Bushnell, BSG

unread,
Jun 22, 2016, 11:41:07 PM6/22/16
to Michael Jones, Viktor Kojouharov, golang-nuts

This is why I like this example. Generics induce people to write the wrong thing instead of thinking about the right thing. When you start thinking of the right thing, you often discover better solutions.

I think the sort package's way of doing sorting and binary search are so clearly better than almost any generic implementation I've seen, that I think generics are a bad idea for this reason alone.

Thomas

Henry

unread,
Jun 23, 2016, 12:59:16 AM6/23/16
to golang-nuts

Having completed an earlier project, my colleagues and I sat down to review the team performance and what we could do better next. My colleague proposed an intriguing idea and we decided to randomly pick two components of our past projects that could be implemented independently from the rest of the projects and were easy to test. We rewrote the components from using OO (object oriented) to using PP (procedural). I was surprised with the result. The number of lines was reduced by about 30%. We were able to see things that were hidden behind the complex interaction of objects before, and simplified the codes considerably. Performance was improved by about 5%. This was no surprise because I always knew that OO has a bit of an overhead and the code simplification also helps. Maintainability is a bit subjective, but my colleagues and I think that the PP codes are easier to read and simpler to understand. The main obstacle in OO is with understanding the model employed. In our case, we has components that were outsourced to some offshore developers before, and we often had to rely on external documentation to understand the model they use. What was intuitive to them was not very intuitive to us. With PP, my other colleague who was not involved with rewriting the codes thinks that our PP codes can be understood by reading the source codes alone. However, the main selling point to me is the reduction in loc. For every 1000 lines of OO codes, we can save about 300 lines of codes with PP.

This experience makes me question many things and wonder whether the advances in programming for the past 40 years are really improvements over earlier methodologies. Everything I was taught of in college now seems questionable. Some people may argue that -in my case- it is probably a case of bad OO design and that if they get it right, OO should yield substantial benefits, especially when it comes to maintainability. The key phrase here is 'if they get it right'. OO allows abstraction, including over-abstraction, that is limited only by imagination. How do you know when you reach the right design?  

Returning to the topic, instead of arguing about hypothetical uses of generics, we should test it in real projects and see whether the benefit of generics outweighs its cost. There are other languages that support generics and may be used to test this. I personally have doubts over the necessity of generics. I think if they get it right, generics should be beneficial. The key here again is 'if they get it right'.  The room for potential abuse of generics which leads to incorrect design probably outweighs its benefits. 

Egon

unread,
Jun 23, 2016, 6:35:41 AM6/23/16
to golang-nuts, michae...@gmail.com, vkojo...@gmail.com
On Thursday, 23 June 2016 06:41:07 UTC+3, Thomas Bushnell, BSG wrote:

This is why I like this example. Generics induce people to write the wrong thing instead of thinking about the right thing. When you start thinking of the right thing, you often discover better solutions.


It's also a great example why real-world examples are better for discussion.
 

I think the sort package's way of doing sorting and binary search are so clearly better than almost any generic implementation I've seen, that I think generics are a bad idea for this reason alone.


To me the major issues with C++/Java/C# generics is that they are too fine-grained. Often they are declaration scoped with meaningless type parameter names "T", "K".

The most promising generics idea I've seen is packages with interface specialization. 1. it's harder to abuse, 2. you end up building packages as usual with interfaces, 3. minimal change to the language. However it has some drawbacks - it doesn't solve all the generics problems - only data structures and algorithms part. The generic "sort" package, with that approach, wouldn't look any different.

Egon

unread,
Jun 23, 2016, 7:29:29 AM6/23/16