I am not in favor of generics.

1,220 views
Skip to first unread message

jucie....@zanthus.com.br

unread,
Sep 17, 2018, 12:04:26 PM9/17/18
to golang-nuts
Go core team is working hard to bring generics to the language because several people asked for it. With all due respect for those users and for people working hard to make generics a reality, I feel that a greater number of people would suffer after generics adoption. So, I feel compeled to manifest my opinion: sorry guys, Go with generics will be worse than Go without generics.

The language strives for simplicity since its inception and that is what attracted a large part of its user base. We must think about who we will want to have in our community 10 years from now. Supporting generics would please a minority to the detriment of a large number of potential users.

Today Go is easy to learn and tools are easy to implement. Please keep it that way.

Thanks.

Eric Johnson

unread,
Sep 17, 2018, 2:18:51 PM9/17/18
to golang-nuts


On Monday, September 17, 2018 at 9:04:26 AM UTC-7, jucie....@zanthus.com.br wrote:
Go core team is working hard to bring generics to the language because several people asked for it. With all due respect for those users and for people working hard to make generics a reality, I feel that a greater number of people would suffer after generics adoption. So, I feel compeled to manifest my opinion: sorry guys, Go with generics will be worse than Go without generics.

I think you're right, that adding parameterized typed method invocations will add complexity, and therefore people will suffer more. However, to me, it isn't obvious, without a final proposal against which to assess that concern, that the pain will outweigh the gain.

Certainly, the existing proposal suffers from at least one area of considerable additional complexity - "contracts" - which is both a great way to address the problem space, and an area of new complexity incompatible with Go's existing language choices. I look forward to seeing the next round of drafts and how they incorporate concerns about contracts.
 

The language strives for simplicity since its inception and that is what attracted a large part of its user base. We must think about who we will want to have in our community 10 years from now. Supporting generics would please a minority to the detriment of a large number of potential users.

What pushes me in the direction of encouraging attempts to address the missing language feature of generics are two aspects:
  • Copy and paste coding is an enormous source of errors, and to the extent that generics addresses some of those scenarios, that is a big win for the language and the developers. Writing yet another "for ... range" loop to perform the same mutation on a very similar type is not a big deal the 2nd time, but by the time I've had to do that for a fifth time, I'm unhappy about it, for sure. (YAFRL - "yet another for ... range loop")
  • Go already has "generic" capabilities. "range", channels, select, and type-casting are generic language constructs, "Make", "copy", "append" all provide generic functions, and maps and slices are generic data structures. 
Developers certainly do not have a problem using the existing "generic" capabilities of the language, so I think there's a case to be made that they will not consider additional "generic" capability that much harder to absorb.


Today Go is easy to learn and tools are easy to implement. Please keep it that way.

I agree with that sentiment!

Eric.

Wojciech S. Czarnecki

unread,
Sep 17, 2018, 5:21:03 PM9/17/18
to golan...@googlegroups.com, Eric Johnson, jucie....@zanthus.com.br
On Mon, 17 Sep 2018 11:18:51 -0700 (PDT)
> Eric Johnson
>> jucie....@zanthus.com.br

I hope that the team will make an informed decission and assesment taking
into account both the opinion of the community and its good ideas to make
something simpler. See all community proposals at
https://github.com/golang/go/wiki/Go2GenericsFeedback

> adding parameterized typed method invocations will add complexity,

Not all proposals need this. Eg. CGG https://github.com/ohir/gonerics [mine's]
does not change user code syntax at all. A few changes were needed for
the generic code implementation but it upkeeps Go's philosophy. I hope.
If it is not, please tell me.

> incompatible with Go's existing language choices. I look forward to seeing
> the next round of drafts and how they incorporate concerns about contracts.

>> Today Go is easy to learn and tools are easy to implement.
>> Please keep it that way.

> I agree with that sentiment!

So do I.

Christopher Sebastian

unread,
Sep 18, 2018, 6:19:29 AM9/18/18
to golang-nuts
When I was a new Go user and didn't fully understand how to use the language, I really wanted Generics.

But the more Go experience I gained, the more I realized that Generics wouldn't have helped nearly as much as I had assumed; in fact, they would have added significant complexity and probably led me to write more-difficult-to-understand code.

The longer I use Go, the more beautiful it becomes.  It's already elegant and powerful, and we shouldn't add fancy features just to please a crowd.

Thank you Go Team!
~Christopher

alan...@gmail.com

unread,
Sep 18, 2018, 7:04:15 AM9/18/18
to golang-nuts
Although I respect the opinions expressed here, I think you might be pleasantly surprised by how the proposed design would dovetail with the rest of Go and make a number of things much more convenient than they are at present.

It would be nice, for example, to have a full range of collection types in the standard library without the need to use interface{}, type assertions and the performance overhead of 'boxing'.

It's not an ugly design with angle brackets all over the place and most of the time you'd hardly notice you were using a generic function as the type parameter(s) would be automatically inferred from usage.

Better still it would be compatible with Go 1.

Admittedly, there's a lot of discussion over the design at present though that's mainly about the constraint system. Everybody agrees that this needs to be both simple and  expressive though opinions differ over the best way to achieve that.

Anyway, as I said in another thread, the important thing is that the existing built-in generic stuff is not interfered with, so those who prefer to avoid generics may do so and carry on as they are. That way everybody will be happy :)

Alan

Chris Hopkins

unread,
Sep 18, 2018, 7:23:16 AM9/18/18
to golang-nuts
+1

IMO the place generics would be useful is reducing the use of the empty interface and then a type switch. Otherwise I don't see quite what people are doing that wouldn't be better done with interfaces.
In the past I mistakenly tried to declare local methods on the builtin types. Revisiting my ignorance I wonder if that would help though.

I see the logic that "append and len and other inbuilt functions are generic and idiomatic, so it would be better to allow others to write their own generic functions". However I would argue that the problem could be turned on its head. It's not that other types need to be made more like the inbuilts, but that inbuilts become more like all other types. Why doesn't append have an interface requirement like the sort interface? 
Anyway I realise I don't understand the problem enough to wrap this up in an official proposal, but I see it as it is things like channel operations and arithmetic that are unusual and should use interfaces (under the hood), rather than everything else fitted around them.

But this is probably me not understanding the problem that people are trying to solve. It's more than people wanting to neither use an empty interface, nor declare their interfaces isn't it?

Chris

Chris Hopkins

unread,
Sep 18, 2018, 7:29:37 AM9/18/18
to golang-nuts
Pondering this, my concern is that it might become too powerful. I'm scared this will make it harder to work out what someone has done if there's too much indirection and magic added on top. It feels like it could be a really *really* big hammer.

I don't buy the argument " those who prefer to avoid generics may do so and carry on as they are" because go is all about large projects and code reuse. If you enable people to write code that the average programmer can't understand, then IMO that is against the philosophy of what i thought go was.

Knuth save me from people writing "clever" code.

Chris

ffm...@web.de

unread,
Sep 18, 2018, 7:39:12 AM9/18/18
to golang-nuts


Am Dienstag, 18. September 2018 13:29:37 UTC+2 schrieb Chris Hopkins:
Pondering this, my concern is that it might become too powerful. I'm scared this will make it harder to work out what someone has done if there's too much indirection and magic added on top. It feels like it could be a really *really* big hammer.

I'm not so much scared in this way. Every average Joe Java boilerplate coder gets along with generics. It only gets a little tricky with inheritance. But even there you can manage without thinking and just doing trial and error if you wanted to. Besides that, Go does not have inheritance. So, I think it will neither be tricky to make use of nor to read the code.


Hoo Luu

unread,
Sep 18, 2018, 8:15:22 AM9/18/18
to golang-nuts

在 2018年9月18日星期二 UTC+8下午7:29:37,Chris Hopkins写道:
Pondering this, my concern is that it might become too powerful. I'm scared this will make it harder to work out what someone has done if there's too much indirection and magic added on top. It feels like it could be a really *really* big hammer.

I don't buy the argument " those who prefer to avoid generics may do so and carry on as they are" because go is all about large projects and code reuse. If you enable people to write code that the average programmer can't understand, then IMO that is against the philosophy of what i thought go was.
 
+1.  Go should not become another C++, of which different people or groups use different subsets.

Thomas Bushnell, BSG

unread,
Sep 18, 2018, 8:49:27 AM9/18/18
to alan...@gmail.com, golang-nuts
On Tue, Sep 18, 2018 at 1:04 PM <alan...@gmail.com> wrote:
It would be nice, for example, to have a full range of collection types in the standard library without the need to use interface{}, type assertions and the performance overhead of 'boxing'.

From the earliest days of Object Oriented Programming, the idea of abstract data types and generic implementations of container classes has stood out as the one (and virtually only) win for this strategy.

So let's first notice the failure: numeric types. It seems like numbers, which fit nicely into set-theoretic classes, should be a great use of Object Oriented structure, but as soon as you realize that the addition operation needs to inherit from two separate classes a problem happens. Smalltalk coped by having a global namespace of "priorities" for numbers, and each numeric operation promoted the receiver or the argument as necessary, with all kinds of really annoying manual work of just the sort that dynamic dispatch was supposed to make unnecessary.

So Lisp had generic functions which could genuinely parameterize on the types of more than one argument, but that still produces a disaster; to implement addition when you have N different types requires writing N-squared little functions. This leads to the code bloat we are familiar with in C++ - but quadratically worse.

So numbers fail. Nobody's got a way that actually works. What about containers? Also a fail. Why? Because the implementation depends on the use.

A brilliant example of this was a proposed spreadsheet design. Having noticed that X provides output windows, which can be independently moved and updated, we'll just implement a spreadsheet by creating ten thousand little X windows, each backed by a separate Unix process, and responsible for displaying their value and communicating across the X server to find the values of cells they depend on. And the server's refresh logic will prevent unnecessary computation! Brilliant! And nothing in the documentation of the X protocol says, "oh, by the way, that would be an idiotic strategy".

This happens everywhere. We have experience that very simple types don't require more: hash tables, arrays, pairs. But for fancy container classes? If you want a "set" type and hash tables aren't for you, that's because hash tables don't support operations like union and intersection. But guess what: every implementation of union and intersection makes assumptions about the expected use, such that misuse will result in something which is mathematically appropriate, but a computational disaster. There is no generic implementation of set theoretic union and intersection which works for all users as well as hash tables currently work for all users.

So yes, you can implement collection classes with generics. But so what? You don't ever want to use them. Either performance matters, and the generic implementation will be unacceptable to anyone who isn't lucky, or performance doesn't matter and you don't need a sophisticated collection class at all. (Or you're in the small well-known set of data types that we are confident can be implemented generically - but this is a small and static set, and does not require general generics as part of the language.)

Thomas

Wojciech S. Czarnecki

unread,
Sep 18, 2018, 8:53:24 AM9/18/18
to golan...@googlegroups.com
On Tue, 18 Sep 2018 04:38:55 -0700 (PDT)
ffm...@web.de wrote:

> Every average Joe Java boilerplate coder gets along with generics.
Barely, if at all, understanding whats under.

"Smart Copy Paste ... A book for normal programmers"
https://www.amazon.com/Smart-Paste-Stack-Overflow-other-ebook/dp/B01EHI5RQM

> But even there you can manage without thinking and just doing
> trial and error if you wanted to.

OP, I and many others want to defend Gol off this very attitude.
Go is among few contemporary languages that allows me to fully
understand what my resulting binary will do, down to the syscall
level. Thats precious.


> I think it will neither be tricky to make use of nor to read the code.

I claim opposite. All and any Gopher will be expected to cope with hard to
read and yet harder to understand generic code. Even if she herself will
vowed not to use it. In a short while we all will be obeying first Java's
commandment "thou shalt not peek under".


--
Wojciech S. Czarnecki
<< ^oo^ >> OHIR-RIPE

Robert Engels

unread,
Sep 18, 2018, 9:19:34 AM9/18/18
to Wojciech S. Czarnecki, golan...@googlegroups.com
Here is a very common problem with generics...

Everyone understands the concept of a tuple/pair. Pretty easy, you have getElement0 and getElement1 methods.

Now a piece of code needs a KeyValue pair used in a map. So, with generics someone says, oh, I already have this pair class, I’ll just use that and declare my types, so Pair<int,string>.

This is wrong, you need to write the code for KeyValue that contains a Pair, with KeyValue<int,string>

The former would work but then the code is littered with getElement0 instead of getKey

It works but much more difficult to understand and maintain.

So even when using generics you still need to be able to write understandable code.

Having a generic KeyValue pair when working with maps makes the code much more readable and type safe than for example working with sync.Map today.

Generics are wonderful when done simply. Because of Gos static compilation and coupled with a user base that doesn’t truly understand the Go performance profile (hint, it is not as fast as Java and never will be because it can’t - but more importantly it doesn’t matter) Go is going to end of with something as complex and obtuse as C++ templates (with concepts).

Not a good fit for Gos development community or its long-term viability. Go doesn’t need generics, at least not full featured ones.
> --
> 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.

Robert Engels

unread,
Sep 18, 2018, 9:21:32 AM9/18/18
to Wojciech S. Czarnecki, golan...@googlegroups.com
I am going to refer everyone involved in this discussion on generics to this once again. I know it is long, read the summary... but it’s important:


On Sep 18, 2018, at 7:52 AM, Wojciech S. Czarnecki <oh...@fairbe.org> wrote:

Wojciech S. Czarnecki

unread,
Sep 18, 2018, 10:51:33 AM9/18/18
to Robert Engels, golang-nuts
On Tue, 18 Sep 2018 08:19:13 -0500
Robert Engels <ren...@ix.netcom.com> wrote:

> So even when using generics you still need to be able to write
> understandable code.

**need to be able to write understandable code**
Yes. This thread is about whether within Go generics proposal this is possible
and if not possible - as it seems - how to prevent disaster that might stem
from current proposal's adoption.

> Go doesn’t need generics, at least not full featured ones.
:) Thank you for your support.


P.S.
> it is not as fast as Java and never will be because it can’t

Depends of metrics and requirements. Considering industry's PoV Go so far is
the fastest language available. While Java indeed can be faster at
code execution, Go can be tenfold faster regarding "dev warmup",
compiling, testing and deployment times. And that matters the most to the
most of users. It is yet more apparent within product's evolution and
maintenance periods.

alan...@gmail.com

unread,
Sep 18, 2018, 11:26:45 AM9/18/18
to golang-nuts
One thing I've always liked about Go is the ability to define your own types based on the built-in ones. Although the built-ins don't have methods themselves, you can still add them to your own types and satisfy interfaces in that way.

This enables us to define simple collection types such as stacks and queues whose underlying type is a slice of values and give them methods such as Push, Peek and Pop. We then no longer need to think about how to achieve these operations by operating on the slice directly.

The trouble at the moment is that if you want, say, a stack of ints or a stack of  strings, you either have to copy what is essentially the same code for each type or use interface{}.

If generics is added, you'd just need to declare your variable to be a stack(int), stack(string) or stack(whatever) and you'd then have a type-safe, performant container without further ado.

Now, even if there was only one constraint - declaring that a type was 'Comparable' i.e. supported the == and != operators, I still think that generics would be worth adding for all the scenarios which that would enable.

You only need to look at the standard libraries of other popular statically typed languages such as Java and C# to see what can be achieved with little or no constraints.

However, in an ideal world, there's lots of other stuff you'd like to do as well and this is where the problems start. To take a very simple example, if you want to write a generic Sum function which can sum the values of a slice of any numeric type, then you need to use the '+' operator and you therefore need some way of telling the compiler that you only want to use types which support that operator.

Now (ignoring 'string') the only types which do support '+' are the built-in numeric types (integer, floating point, complex) and any types you define yourself based on them.

There's no way that your generic Sum function can deal with big.Int because it doesn't have a '+' operator and Go doesn't support operator overloading. Using an 'Add' method instead won't work as int, float64 etc. don't have methods.

So, when Thomas says in his post that numbers are a problem - he's right, they are - and, in particular there's a dichotomy between the built-in and user-defined numeric types.

However, I don't think this is a reason for giving up altogether on this - it's still useful to have generics for the built-ins only. But you do need to come up with a constraint system which is simple enough for everybody to understand but expressive enough to do what you want and I have faith that the Go team will eventually converge on such a system even if the initial draft (IMO) falls short on the simplicity front.

In response to worries that a large Go team won't be able to work together if some use generics and some don't, I would make these points:

1. In my experience of various other languages, most folks don't write their own generic types/functions - they just use a library. I don't necessarily see it as a problem if one programmer prefers to use a generic library and another uses a non-generic library and, as I said earlier, I suspect the latter might be less adverse to generics when they see how convenient they are to use.

2. There is no way that the Go team are going to adopt generics in such a way that it is not a good fit with the rest of the language. So any worries that Go is going to end up like C++ (with everybody using a different subset of the language) or Python (with the version 2/3 split) are unfounded in my opinion.

Alan

robert engels

unread,
Sep 18, 2018, 11:48:56 AM9/18/18
to Wojciech S. Czarnecki, golang-nuts
I don’t think there is any evidence as far as compiling, testing and deployment times.

Deployment is definitely simpler for a small app (single binary), if not, the configuration issue is probably the same. Also, people don’t talk about what will happen when security bugs are discovered in the Go runtime/stdlib ? You need to recompile and redistribute all of the binaries ever built… With Java you just update the JVM, and there have been efficient processes for doing this for decades.

Warm-up time for optimal code execution in Java is still and issue but that has been addressed pretty well in IBM’s J9 JVM, and also the Azul Zing with ReadyNow!.

The lack of declared interfaces and limited, untyped error handling in Go make it HARDER for large applications during evolution and maintenance, not simpler (as compared to Java).

Go excels for systems level processes - small in functional scope - where memory safety and performance matter.


> --
> Wojciech S. Czarnecki
> << ^oo^ >> OHIR-RIPE
>

Wojciech S. Czarnecki

unread,
Sep 18, 2018, 11:59:18 AM9/18/18
to golan...@googlegroups.com, alan...@gmail.com
On Tue, 18 Sep 2018 08:26:29 -0700 (PDT)
alan...@gmail.com wrote:

> There's no way that your generic Sum function can deal with big.Int

Oh, with CGG (https://github.com/ohir/gonerics) of course there is:

func (x type []K) Sum() (r type K) {
for type switch {
case K range int64(), uint64(), float64(), complex128():
for _, v := range x {
r += v
}
case K big.Int:
for _, v := range x {
r.Add(r,v)
}
break // or return in every case instead
}
return
}

:) Your welcome.

> Alan

robert engels

unread,
Sep 18, 2018, 12:10:23 PM9/18/18
to Wojciech S. Czarnecki, golan...@googlegroups.com, alan...@gmail.com
I’ve said many time, the CGG example you cite here is not generic code. If I want to pass a different user type and use that method I can’t, I need to change the source of Sum(). That is not generic programming...

Wojciech S. Czarnecki

unread,
Sep 18, 2018, 12:58:23 PM9/18/18
to robert engels, golang-nuts, alan...@gmail.com
On Tue, 18 Sep 2018 11:10:00 -0500
robert engels <ren...@ix.netcom.com> wrote:

> I’ve said many time, the CGG example you cite here is not generic code.

I do not want neither write nor **READ** Go++ code.

CGG is called Craftsman's for a reason.

Its for writing and **reading** real production code. It disallows, by the
very construction, fancy recurrent types and other metaprogramming that
allows "clever guys" to encipher unreadable math with yet less readable
syntax.


> I want to pass a different user type and use that method I can’t, I need to
> change the source of Sum(). That is not generic programming...

Wrong! CGG allows operations in terms of base type (thats the num case) And
in terms what a type have or can. It's just a matter of contract.

Any person who actually got to **read** and understand CGG proposal would
spot the possibility of more generic contract by oneself:

func (x type []K) Sum() (r type K) {
for type switch {
case K range int64(), uint64(), float64(), complex128():
for _, v := range x {
r += v
}

// case K big.Int: was a simple example for Alan's stated problem

// lets make it more general for Robert:
// allow for any type that has a Sum (to itself) method:

case K (*K) Add(K, K):
for _, v := range x {
r.Add(r,v)
}

// Or, that has a value method that returns sum with self
case K (K) Add(K) K:
for _, v := range x {
r = r.Add(v)
}
break // or return in every case instead
}
return
}

> I need to change the source of Sum().
It is **me** as a generic implementer to change the source of Sum, or to
write it for some special cases as the value method case above.
If its in a lib, raise issue. I'll answer why I have omitted your outstanding
type. Or I'll implement it.

In either case, you can see the source and infer whether your type will pass
just by reading straight Go code slightly guarded by the constraints on
**types**.

mhh...@gmail.com

unread,
Sep 18, 2018, 1:11:52 PM9/18/18
to golang-nuts
I agree with Robert, this is not re usable.

I much prefer this

func Sum(some []K, add func(l,r K) K) (ret K) {
  for _, v := range some {
    ret = add(ret, v)
  }
  return ret
}

func main(){
  total := Sum([]int{1,2,3,4}, func(l,r int) int {return l+r})
}

Eric Johnson

unread,
Sep 18, 2018, 1:38:34 PM9/18/18
to golang-nuts
On Tue, Sep 18, 2018 at 6:21 AM Robert Engels <ren...@ix.netcom.com> wrote:
I am going to refer everyone involved in this discussion on generics to this once again. I know it is long, read the summary... but it’s important:


I'm skimming that paper. Thanks for sharing. Paper definitely identifies benefits for adding generics.

This quote jumped out at me for its relevance to this discussion:
"We observed parameterization of 1152 types, but actually found about 46 % of these types (532) only had exactly one type argument ever used throughout the project’s history, suggesting that needless or premature generification of objects occurs fairly frequently.We observed parameterization of 1152 types, but actually found about 46 % of these types (532) only had exactly one type argument ever used throughout the project’s history, suggesting that needless or premature generification of objects occurs fairly frequently."

Put in the term of this discussion thread, the study notes that about 45% of the time (for the Java projects studied), portions of the code get harder to read, with little or no clear benefit. There's no basis for thinking that the same problems will or won't occur with any generics added to Go, but it seems like a reasonable place to point to for concerns driving this thread.

Eric.
 
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/3ia8XrUgqOg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

alan...@gmail.com

unread,
Sep 18, 2018, 2:53:10 PM9/18/18
to golang-nuts
Eric,

This is the sort of situation where a tool could help.

If the tool detects that you've written a generic type/function but only then instantiated it once, that could lead you to rewrite the type/function non-generically for the type(s) actually used in the instantiation.

Alan

Michael Jones

unread,
Sep 18, 2018, 4:06:10 PM9/18/18
to alan...@gmail.com, golang-nuts
Thomas, Good morning old friend. 

Yes the implementation of axiomized arithmetic has proven difficult. Not that it is too hard, but because (I think) it requires a mindset alien to most developers. It works very well in the symbolic algebra system Axiom which is the acme; A matrix is not just "a matrix" but a 2D "matrix of integers taken from a specified field." The precision and clarity are high and the thinking required to state just what things are is also high. This works here, and pretty well in Mathematica, but really has no counterpart in computer arithmetic where even addition may violate the rules of mathematics.

It would be nice to say that a Complex() or Quaternion() was a tuple of Numbers() and have it be true that fully performant big float complex math could be expressed that way, Very nice indeed. Something I look at,. But, it seems ambitious and suggests that built-in complex types still have a home.

I believe average Go code will be better if there are map-quality llrb trees, stacks, sets, ... for the taking, just as if tuples were a first class thing. There were horrors in STL's evolution, but by now, everyone gets such data structures right the first time they use them, just as they do now with Go maps.

Michael
Michael T. Jones
michae...@gmail.com

Wojciech S. Czarnecki

unread,
Sep 18, 2018, 4:54:02 PM9/18/18
to golan...@googlegroups.com, mhh...@gmail.com
On Tue, 18 Sep 2018 10:11:52 -0700 (PDT)
mhh...@gmail.com wrote:

> I agree with Robert, this is not re usable.

What is not reusable? A generic Sum function allowed by
the CGG that can sum any type of any base that accidentally
has anything countable in it?

> I much prefer this
>
> func Sum(some []K, add func(l,r K) K) (ret K) {
> for _, v := range some {
> ret = add(ret, v)
> }
> return ret
> }
>
> func main(){


> total := Sum([]int{1,2,3,4}, func(l,r int) int {return l+r})
> }

How clever and generic it looks. Generic, javish and cpluplusish.
And how readable call site it gave.

In CGG Go user code call is exactly the same for ANY type.

total := Sum(x) // For var x []AnyType. ANY.

var x []string
total := Sum(x)

may return sum of all runes in all strings and sprintf-ed to
the resulting string.

> this is not re usable.

How can I call your example to get string result of sum of all
runes? CGG's is as with any other type: `total := Sum(x)`

robert engels

unread,
Sep 18, 2018, 5:58:11 PM9/18/18
to Wojciech S. Czarnecki, golan...@googlegroups.com, mhh...@gmail.com
If you have type checking / casting / type select, whatever you want to call it in the “generic code”, it is not generic, and not re-usable.

You can’t possibly know all the possible types, there may even be more additional primitive types, so the code is not resilient - Go 2.1 and all of your library code breaks?

The caller will not have access to the source code (and certainly not want to read it to call a function). Nothing will be obvious as to what Sum can work on or what it does - in fact you can have very subtle errors because the code is duplicated across types.

Besides those problems, even if it was somehow workable, the syntax reuses keywords in non-standard ways (for example even if this was adopted, why wouldn’t the syntax be:

switch K {
case (int64,unint64,…):
blah blah blah
case big.Int
blah blah blah
default:
panic // implied
}

The compiler already knows K is a type…

And yet more problems, because you probably need every combination of types if it has more than one, (depending on what the method does) — you need an n*n number' of case statements to implement it….

I am just being realistic, and I don’t mean to be harsh, but I don’t think your proposal is going to be accepted, and I think it might be better if you spent more time listening to the criticism, and maybe reworking, than trying to defend it. I guess there is always the chance I don’t get it, but I think I do…

Not sure how else to explain it.

Michael Jones

unread,
Sep 18, 2018, 6:21:29 PM9/18/18
to Robert Engels, oh...@fairbe.org, golang-nuts, mhh...@gmail.com
the way to explain it is this: support for using type-specific-but-parameterized written by A by caller B with their own types not anticipated or known by A; the ability for B to easily say, "I want A's excellent feature and I want to use it with my special B-only data type."

this cannot happen with a switch in A's code unless B rewrites it.

for a fixed set of types, this is possible and good. i rewrote the sort package so that sort.Sort() has just this kind of type switch. if the slice is byte, short, ... float,... string then it dispatches to a type-specific version that is macro instantiated from one of three versions (integer, float, string). this pays the interface tax once rather than n log n times. it is 2x faster plus it is also parallel for another ~ 3/4 ncpus speedup. that's my kind of code. even the most limited kind of generic can handle this use.

but to make it work for a user's own types without code change? that needs a greater magic.

mhh...@gmail.com

unread,
Sep 18, 2018, 6:22:01 PM9/18/18
to golang-nuts
I was referring to this code

func (x type []K) Sum() (r type K) {
 
for type switch {
 
case K range int64(), uint64(), float64(), complex128():
       
for _, v := range x {
          r
+= v
       
}
 
case K big.Int:
       
for _, v := range x {
          r
.Add(r,v)
       
}
 
break  // or return in every case instead
 
}
 
return
}

I did not understand how "// For var x []AnyType. ANY. " relates to previous code.

for your question about the rune, i guess it d be like this

func Sum(some []K, add func(l,r K) K) (ret K) {
 
for _, v := range some {
    ret
= add(ret, v)
 
}
 
return ret
}


func addOperator
(l,r Z)Z{
   
return l+r
}

func main
(){
  total
:= Sum([]rune("whatever"), addOperator)
}

Consider Z,K as virtual types to be inferred from the call site.

In regards to Russ Cox proposal this code is missing
their explicitly declared contracts and the function instances declaration.
On the other hand, this is so much more readable to me.

maybe it looks likes c++, i did not think it was a concern
as long as it respected desired behaviors of good error reporting,
good readability, ease of usage, speed of compilation,
proven useful and workable etc

in regards to all those factors Russ Cox knows way better so he s probably right,
yet the link provided by Robert is extremely interesting, repeating it here

This was also an interesting reading

Just quoting from it

Frankel [Fra 9 3]found that generics were not
widely used in Ada. Later, the principal designer of Ada suggested that, if he could,
he would eliminate parameterized types, because they were “less useful than originally
thought” [RSB05].

Anyways, I am polluting the thread against this proposal, sorry for that.

Wojciech S. Czarnecki

unread,
Sep 19, 2018, 5:26:35 AM9/19/18
to golan...@googlegroups.com, mhh...@gmail.com, Robert Engels, Michael Jones, alan...@gmail.com
> On Tue, 18 Sep 2018 15:22:01 -0700 (PDT) mhh...@gmail.com wrote:

The **stated** goal for adding some kind of generics to Go is
and always was "how to not repeat writing same code just for a type".

Now above practical goal, in minds of many, somewhat morphed
to "Yeah, lets have real true generics so we can write/reuse
smart and clever code we once upon a time wrote for c++/java".
"So we need a way of expressing generic data in terms of other
generic data then use abstract alghoritm on that".
(Yep, team's proposal use: "in terms of operations")

It now poisons all the proposals I've seen with syntax that
forces **user** of the code to **repeat** at the **call site**
type identifier for the sake of "code instantiation".

Why? I think that's a mark of "one proper way for doing generics" burnt
on the popular mindset. As hard to overcome as notion that one needs
'implements/implementing' to use interfaces.

Does Go2 should have "implements" keyword added too?
`type mytype struct{...} : implements io.Writer, ..., ...`

What?! "Compiler knows whether your type satisfies an interface"

Yeah, and at the call site compiler **does know** the parameter
type too. This is the CGG basis.


1. Find a way to write "some Go code" that is suitable for set of
types that have "something" in common.

2. Find a way to describe the set via the "something common"
that implementation for said set will use on objects of matching types.

3. It **MUST** read as Go: no instantiation at call site.


On Tue, 18 Sep 2018 15:22:01 -0700 (PDT)
mhh...@gmail.com wrote:

> I was referring to this code
>
> func (x type []K) Sum() (r type K) {
> }
>

> I did not understand how "// For var x []AnyType. ANY. " relates to
> previous code.

Previous code example was specific to `big.Int` type. Next I gave
a really generic usage example via the constraint that already is
described in CGG proposal: "type with given method implemented".

With that constraint func Sum can be called on _any_ type that has
a method (*T) Add(T,T). And someone writing new code needs not to
change generic lib (as imputed), but she needs to provide for her
non-numeric or peculiar type a method (*T) Add(T,T). Then Sum works.

> for your question about the rune, i guess it d be like this
[...]
> Consider Z,K as virtual types to be inferred from the call site.
Progress! As to inferred :)

But I wanted return to be a string with sum of runes from all
input strings :) Leave it.

In CGG such specialization takes place in a single to find and simple
to **read** 'case' code. The proposal's example Sum already has a single
piece of code that will be **reused** for all numerical types that user
**might** declare off builtin. With only two for type cases CGG's generic
Sum spans all types with numerical base and all types that provide their own
"me plus operator". With one case more it allows for set of types that have
"plus me operator". Code that can and will run for multitude of user types.

Yet I still hear stubborn "not reusable!, not reusable! not generic!".

> In regards to Russ Cox proposal this code is missing
> their explicitly declared contracts and the function instances declaration.
> On the other hand, this is so much more readable *to me*.

The "real true generics" proposals are readable to people who took the
time and the training to accommodate to such List<Map<Hashable,Tuple>>
syntactical mess. But it is **against** Go philosophy of "readability first".
It excludes the legion who finds mental instantiations cumbersome at
reading time. It shifts their thought from "what this will do" to "in what
shape it will be".


> maybe it looks likes c++, i did not think it was a concern
> as long as it respected desired behaviors of good error reporting,

It IS a MAIN concern. In fact it is a concern that the Go
team explicitly stated as a reason why Go1 is generics free.

> good readability,
It has **no** readability for all but c++/java conditioned
and is an inexhaustible source of confusion in other languages

> ease of usage.
leading to ubiquitous C&P in java/c++ application of generic libs.

> speed of compilation,
[I assume it was not about c++ compiling]

With CGG a single significant change to current gc parsing will
be in marking 'func' node so parser could swap the subtree when
it will see 'for type' while in declaration body.

The only bigger chunk of new code in gc will be for contract
parsing and matching. Everything else needed for fast CGG
implementation already is working in the current gc.

[ c++ and like approach ]
> proven useful
Proven as a source of confusion and bugs in analysis
linked above in thread (thanks for the link, Robert :).

> and workable etc
Workable and ubiquitous. Like 'implements' before Go.

> Anyways, I am polluting the thread against this proposal, sorry for that.

It is not a pollution, really. I am glad I can discuss both "we need no
generics" and "we need practical approach" topics with avid users of
c++/java style generics. It may show me how to simplify and specify
my description of "craftsman's ways" to all c++<enslaved> :)

P.S. (All) Take my apologies for "javish and cpluplusish" sarcasm.
I just am a bit resentful when I am pushed to fight strawmen about
CGG possibilities. Described, tabularized even, but not read. Not
to mention understood.

I am patiently waiting for someone who at last will use the slider
and will read CGG proposal past the top example. Someone
leaving "it's not about generics I know" impulses aside.

CGG is not about known, CGG proposal is about reusable code
written "in Go I know" hence easy to read and understand.


Peace,

Robert Engels

unread,
Sep 19, 2018, 7:40:51 AM9/19/18
to Wojciech S. Czarnecki, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
Go not having implements is a big problem when refactoring large Go systems especially because it doesn’t have generics - all type safety is gone and you fly by the seat of your pants.

Robert Engels

unread,
Sep 19, 2018, 7:52:31 AM9/19/18
to Wojciech S. Czarnecki, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
Also, I did read what you wrote. I questioned “what happens when you have N different generic types in the call signature. The select type statement has N*N cases. “. You did not respond.

On Sep 19, 2018, at 4:26 AM, Wojciech S. Czarnecki <oh...@fairbe.org> wrote:

prade...@gmail.com

unread,
Sep 19, 2018, 8:17:33 AM9/19/18
to golang-nuts
Anyone who doesn't want some form of generics is in favor of a bad status quo. Now I'll be fine without user defined generics if Go provided a dozens of built-in compile time safe generic data structures like trees, sets, tuples, ordered maps, weakmaps perhaps WITH a complete set of methods like map/reduce/filter/... it would be a good compromise. 

I'd be fine, if generics could only be functions as well and not structs with generic methods.

But the whole, "just use interface {}" gimmick in a statically typed language? NO.

Thomas Bushnell, BSG

unread,
Sep 19, 2018, 9:40:08 AM9/19/18
to Robert Engels, Wojciech S. Czarnecki, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
Huh? Type safety is still checked by the compiler. Implements does nothing except put a road-block in the way and prohibit you from making an interface that some other package happens to implement.

robert engels

unread,
Sep 19, 2018, 10:04:50 AM9/19/18
to Thomas Bushnell, BSG, Wojciech S. Czarnecki, golang-nuts, mhh...@gmail.com, Michael Jones, alan...@gmail.com
That is not true. Often, due to lack of generics things are passed as interface{} or a sub interface, and then type checked/casted in the method. When you refactor you will get no warnings that X no longer meets the contract. You have no obvious way either by looking at the code to know which interfaces a developer needs (or wants it) to implement. Things will fail at runtime,

People state this is a plus - yes it is - for small easily contained/understood systems. As the application grows larger, this is a real bottleneck to productivity.

Having ‘implements’ does nothing to harm the productivity - not having it hurts.

The opinion that well, since there is no implements I can define my own interface, and pass some stdlib struct that I can’t control as an “implementor” is hogwash. Because you also don’t control this code, the API is free to change - breaking your code. This is why the “backwards implements” is a bad idea.

Having implements is the easiest to understand large systems - if you use interface based design, you can get a full mental picture of the system, then by looking at implementors you can determine the concrete options (performance, capabilities, etc.)

Try working with any dynamic language that has duck typing - it is impossible to refactor. Don’t take my word for it, just read any of the thousands of blog posts covering it.

Go is no different.

Go was designed to replace scripting tools, and C tools (usually systems level). It was not designed for large enterprise applications, and that is why it falls down in many places. It doesn’t mean it isn’t GREAT for what it was designed to do, and better than Java in many (most) of these cases. Large enterprise apps, no way.

Wojciech S. Czarnecki

unread,
Sep 19, 2018, 10:26:10 AM9/19/18
to Robert Engels, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
On Wed, 19 Sep 2018 06:52:09 -0500
Robert Engels <ren...@ix.netcom.com> wrote:

> Also, I did read what you wrote. I questioned “what happens when you have N
> different generic types in the call signature. The select type statement
> has N*N cases. “. You did not respond.

The object code (i.e. ssa then machine) is instantiated **only** when its
**used** with given types set.

If you really do call f.genpkg.(x) method on 200 different types for f,
each with 200 N typed vars x you will pollute symbol table **at compiling
time** (40000 entries you will not see until you dump the table).

But resulting object code will have at most as many variants as M
and N have different base types / for-type-cases at most.

It further can be size-optimized by factoring out type-sized operations
reusing a given code chunk for more than one base/case.

Hope this helps,

Robert Engels

unread,
Sep 19, 2018, 10:32:57 AM9/19/18
to Wojciech S. Czarnecki, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
This is not what I am referring to. I am stating that with N generics types in a method the developer needs to write NxN case methods. Not feasible.

Sent from my iPhone

Space A.

unread,
Sep 19, 2018, 10:46:15 AM9/19/18
to golang-nuts
+1 Go doesn't need generics, in fact it goes against its philosophy and core values.
Lack of generics was one of the reasons I moved to Go, I worked with Java since version 3, and I do believe that generics just made Java worse.

Regards.



понедельник, 17 сентября 2018 г., 19:04:26 UTC+3 пользователь jucie....@zanthus.com.br написал:
Go core team is working hard to bring generics to the language because several people asked for it. With all due respect for those users and for people working hard to make generics a reality, I feel that a greater number of people would suffer after generics adoption. So, I feel compeled to manifest my opinion: sorry guys, Go with generics will be worse than Go without generics.

The language strives for simplicity since its inception and that is what attracted a large part of its user base. We must think about who we will want to have in our community 10 years from now. Supporting generics would please a minority to the detriment of a large number of potential users.

Today Go is easy to learn and tools are easy to implement. Please keep it that way.

Thanks.

Thomas Bushnell, BSG

unread,
Sep 19, 2018, 11:02:09 AM9/19/18
to robert engels, Wojciech S. Czarnecki, golang-nuts, mhh...@gmail.com, Michael Jones, alan...@gmail.com
That's a different case than one which "implements" addresses. You don't need "implements" to address that problem, if you think it's a problem.

Adding "implements" doesn't help. I understand there are people who think adding generics might help, but that's a different thing.

Thomas Bushnell, BSG

unread,
Sep 19, 2018, 11:02:58 AM9/19/18
to robert engels, Wojciech S. Czarnecki, golang-nuts, mhh...@gmail.com, Michael Jones, alan...@gmail.com
On Wed, Sep 19, 2018 at 4:04 PM robert engels <ren...@ix.netcom.com> wrote:

The opinion that well, since there is no implements I can define my own interface, and pass some stdlib struct that I can’t control as an “implementor” is hogwash. Because you also don’t control this code, the API is free to change - breaking your code. This is why the “backwards implements” is a bad idea.

I have experience in a millions-of-lines connected code base which uses exactly the strategy that you say can't work. I can confirm that it does work if you use it correctly. 

Robert Engels

unread,
Sep 19, 2018, 11:17:57 AM9/19/18
to Thomas Bushnell, BSG, Wojciech S. Czarnecki, golang-nuts, mhh...@gmail.com, Michael Jones, alan...@gmail.com
There is a reason that Google has moved to Angular2 from AngularJS. As applications get larger you need more structure in order to maintain them. Implements is one of the ways to provide the needed structure. I know having structure goes against hacking. Try having a conversation in a room with speakers in a hundred languages. Not easy. Structure, consistency, common language. Staying explicating what something represents is going to be a better communication tool. 

Sent from my iPhone

Wojciech S. Czarnecki

unread,
Sep 19, 2018, 11:52:21 AM9/19/18
to Robert Engels, golan...@googlegroups.com, mhh...@gmail.com, Michael Jones, alan...@gmail.com
On Wed, 19 Sep 2018 09:32:39 -0500
Robert Engels <ren...@ix.netcom.com> wrote:

> This is not what I am referring to. I am stating that with N generics types
> in a method the developer needs to write NxN case methods. Not feasible.

Wrong. On two axis.

1. As stated in proposal, CGG has no "generic type" (generic data).

CGG stays entirely within comfort zone of the current Go type system.
No new things. CGG allows for readable generic **code** only.

Within CGG proposal you can **not** describe generic data using
blocks of more grained generic data; i.e. in c++/java's matrioshka style.
By design.

2. CGG generic code will operate "on types or part thereof".

Example Sum, in its most compact version

func (x type []K) Sum() (r type K) {
for type K range int64(), uint64(), float64(), complex128()
for _, v := range x {
r += v
}
return
}

operates on any member of **infinite** set of concrete types
that has base type of either: int8, int16, int32, int64, uint8,
uint16, uint32, uint64, float32, float64, complex64 or complex128.

The developer implementing generic solution needs to write
`for _, v := range x {;r+=v;};return;};` source.

Above source makes to object code that will run alike for
`type my1 int8` and `type my2 int64`. Possible that it will be
a single variant for "all signed integers".

( From the contract one can infer, that Sum as stated may have up to 4,
and **at most** 12 emitted variants. None of which written directly
by the developer. )

In the second version, one that is meant to act also on some outstanding
type, the specialization is the devoloper's to decide. She decided to make
it for something like big.Int /T = big.Int/? Hmm.. I only need Add, so make
it more generic. `(*K) Add(K, K)` constraint allows for an x being a member
of infinite set of concrete types that "have method Add" of given signature.
Big.Int just happens to be member of that set.

func (x type []K) Sum() (r type K) {
for type switch {
case K range int64(), uint64(), float64(), complex128():
for _, v := range x {
r += v
}
case K (*K) Add(K, K):
for _, v := range x {
r.Add(r,v)
}
break
}
return
}

CGG allows for specialized generic code. With "all types something that
allowed types have in common" occupying a single case.

Within other generic package, eg. webcommerce one, developer is free
to define and use other Sum - domain specific. Eg one that sums all
posible types of virtual "items" kind:

package "webo"

func (x type []K) Sum() (r int) {
for type K.Value = int()
for _, v := range x {
r += v.Value
}
return
}

this Sum will take as x any struct with Value field that happens
to be int based. So Ticket (s) of below can be webo.Summed too.

type Reward int8
type Ticket struct {
n string
Value Reward

Dave Cheney

unread,
Sep 19, 2018, 12:40:57 PM9/19/18
to golang-nuts
Thank you to everyone who has contributed to this thread.

It is time for everyone to take a break for 48 hours. After this time if you feel strongly that there is a point which you must continue to debate please do so, but be mindful that many words have already been spent in this thread and the points of view expressed are unlikely to change.

Thank you

Dave

Randall O'Reilly

unread,
Sep 19, 2018, 8:19:05 PM9/19/18
to Michael Jones, alan...@gmail.com, golang-nuts
Given all the challenges associated with generics, it might be productive to follow up on this alternative approach of identifying a (small set) of new primitive containers and functions that would otherwise have been written using generics, that are of sufficiently general applicability as to merit inclusion in Go2? I know this kind of discussion is had frequently on this list, e.g., recently in the case of tuples and ternary expressions, and the outcome is usually a pretty convincing “not gonna happen”, but MAYBE in the light of the generics discussion there might be some fresh insights along these lines?

It seems like one of the fundamental reasons generics are so “contrary” for Go is that they require exposing a bunch of low-level internal special-case logic (e.g., operator semantics, etc) in a “meta” explicit way. Doing so in a clean, simple, elegant way may be impossible. The current Go1 alternative strategy is to bury all that logic well out-of-sight and provide a simple, elegant “magic” set of language primitives that seamlessly interoperate with all the other basic types. For those things that are supported in this way, the result is pure joy and simplicity :) So, a logical alternative to generics is to think about anything of sufficient generality that is missing, and just add it in with the same kind of existing magic.

Another way of framing the issue is: if you give people generics, they will each write the same kind of functionality in slightly different ways. Instead, the “Go” way is to figure out the *best* way to do something, and build that right on into the language as a primitive, which fully interoperates and deals with all the necessary idiosyncrasies of the other types. I doubt anyone would argue that the existing “minimal basis set” of functionality is truly minimal and truly covers the vast majority of needs.

The list of plausible additions is likely quite small, and the draft generics proposal suggests what most of these might be:

* binary trees (llrb?) — can anyone really argue AGAINST this?

* sets — really map[T]struct{} seems fine here — probably not worth it?

* linked lists?? probably not given llrb? also I’ve written many stacks and they seem trivial and painless using slices..

* tensors (n-dimensional arrays) — this would be GREAT for numerical stuff — there is an existing heroic implementation https://github.com/gorgonia/tensor but a minimal core builtin support for the basic slice-level functionality extended to multiple dimensions would be really nice! Here’s a proposal: https://go.googlesource.com/proposal/+/master/design/6282-table-data.md

Functions:

* insert() primitive for slices — I wasn’t able to find prior discussion of this, but this is the ONLY major pain point for me in dealing with slices — it is 3 repetitive, relatively opaque lines of code every time, and seems well worth the “investment” in a new keyword.. I did find some debate about whether delete() should operate on slices too — seems like that would be a simple semantic convenience that makes code more explicit and the counter-arguments from the previous debate weren’t that strong IMO https://grokbase.com/t/gg/golang-nuts/1366rkp7ef/go-nuts-why-not-provide-a-delete-function-in-slices-just-like-in-maps

* max(), min(), abs() — how about it? these seem like the main focus of generics discussion, and are a “safe” special case form of the ternary operator. When I first found out that there wasn’t a max operator for ints in Go I was really shocked.. I got over it, but still, these are really basic mathematical operators that I use all the time..

* most of the other examples in the generics proposal seem like such trivial one-liners (sorting, map / reduce, the various channel functions) that it doesn’t quite seem worth it…

Anyway, that is really it for my list — if these were in the language, it would make my code easier and simpler to write and understand. I’m not sure I could say the same for any of the existing generics proposals.. :)

- Randy

David Collier-Brown

unread,
Sep 20, 2018, 8:04:44 AM9/20/18
to golang-nuts
Right now, this looks like the third draft of an academic project.  It suffers badly by comparison to make(), the existing mechanism.

Can we see some more alternatives, please?

--dave

Michael Jones

unread,
Sep 20, 2018, 11:55:04 AM9/20/18
to David Collier-Brown, golang-nuts
[Apologies to Dave Cheney, timely pushback for David Collier-Brown]

David, I disagree and suggest your remark is not well considered. Three replies, one per sentence.

"Right now, this looks like the third draft of an academic project."
Not to me. This reads like years of thought and discussion among qualified researchers and skilled implementers. Not to boast unduly, but if you look at the history of Ian, Robert, and Russ, you'll see intimate responsibility not only in Go but in Gnu C/C++, Modula, and more. What I see in the proposal is informed wisdom. This is engineering so there is always a better way, but this may be the best that can be seen before implementation and experience.

"It suffers badly by comparison to make(), the existing mechanism."
Yes, inherently so. The existing mechanism uses magic in parsing and implementation—details known in the compiler but not exposed in the Go language generally, to make things as easy as possible. It is nice. But from before the launch of Go users asked "how can I do such things generally, in my code, and not by extending the language or the compiler." The answer was "use interfaces now and we'll think about generics later." Later is now and indeed exposing aspects of behind the scenes compiler magic is more complicated, that's inherent. The decision to be made is how powerful vs how complicated.

"Can we see some more alternatives, please?"
They are legion. Existing generics approaches in various languages are more than ideas, they are living examples of design, ecosystem, and behavior. Several proposals have been offered by the Go team and collaborators here. The Go 2 design site lists many specific comments and alternatives. Extensive thoughts have been shared, with responsa sunt, novi in via.

Not a personal attack. Trying to inform these statements and encourage review and consideration.

Michael

--
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.

David Collier-Brown

unread,
Sep 20, 2018, 1:19:27 PM9/20/18
to golang-nuts
Not a problem! 



On Thursday, September 20, 2018 at 11:55:04 AM UTC-4, Michael Jones wrote:
[Apologies to Dave Cheney, timely pushback for David Collier-Brown]

David, I disagree and suggest your remark is not well considered. Three replies, one per sentence.

Not a problem! Indeed, thanks for the response.
 

"Right now, this looks like the third draft of an academic project."
Not to me. This reads like years of thought and discussion among qualified researchers and skilled implementers. Not to boast unduly, but if you look at the history of Ian, Robert, and Russ, you'll see intimate responsibility not only in Go but in Gnu C/C++, Modula, and more. What I see in the proposal is informed wisdom. This is engineering so there is always a better way, but this may be the best that can be seen before implementation and experience.


I've indeed worked with generics in other languages, and always considered that Go, which says it doesn't have generics, has the best set. 

Mind you, the notation is opaque. It says "create me a bunch of X", for a couple of heavily-used meanings of "bunch".  You have to read an article on how it does it to understand the what. 

My usage of generics over the years has tended to collapse from many types originally to just sequences and hash tables. I literally don't remember the last time I did anything more complex in C++ or Java. Maybe a table of sequences, for a library for Steam games.

I don't mean it as an insult to the skills of the community, but I don't see it as an advance, but rather as another experiment in search of an advance.  To restate your "there is always a better way, but this may be the best that can be seen before implementation and experience",  I think it could be the best way before implementation and experience, but I really wish to see more implementations, inclusive of this one, as conscious experiments.  

And I should say that the title doesn't characterize my thoughts (;-)). I am in favor of generics, but I'm cautious in my enthisiasm.

 
"It suffers badly by comparison to make(), the existing mechanism."
Yes, inherently so. The existing mechanism uses magic in parsing and implementation—details known in the compiler but not exposed in the Go language generally, to make things as easy as possible. It is nice. But from before the launch of Go users asked "how can I do such things generally, in my code, and not by extending the language or the compiler." The answer was "use interfaces now and we'll think about generics later." Later is now and indeed exposing aspects of behind the scenes compiler magic is more complicated, that's inherent. The decision to be made is how powerful vs how complicated.

I find the idea of make to be a simple API, just like a library. You ask it for a map of structs indexed by an int, and it gives you one.  The overloading of m = make(map[string]int) versus a = make([]int, 5) makes me wonder what's happening. That, IMHO, is something that needs a better notation, where different parameter lists have differentiable names.

Using an old butt-ugly notation, I would like distinguishable forms like a:= []int.New(5) and m := [int]string.New() 

At that point, the opacity is the same as with an API: it says to create a slice of int of size 5, implemented on an array, or a map of strings indexed by int, implemented as a thing in itself.  If I need more context, I read the code and/or the blogs.



"Can we see some more alternatives, please?"
They are legion. Existing generics approaches in various languages are more than ideas, they are living examples of design, ecosystem, and behavior. Several proposals have been offered by the Go team and collaborators here. The Go 2 design site lists many specific comments and alternatives. Extensive thoughts have been shared, with responsa sunt, novi in via.

Yes, I agree.  Perhaps "ex Africa semper aliquid novi" will be our saving grace, or as the cavalry says, "keep on hacking, Algy" (;-))

Limited objectives, tight control is never fun, but we have something surprisingly good with the magic make() incantation. I'd love to see something that's a single step more general and a bit less like formal magic.

--dave

Wojciech S. Czarnecki

unread,
Sep 20, 2018, 3:41:52 PM9/20/18
to golan...@googlegroups.com
I moved reply to the separate thread.

Subject: "Is "Craftsman's approach" proposal generic enough?"
https://groups.google.com/d/msg/golang-nuts/FOGMEFlL04s/kGQ9mKSsCgAJ

Louki Sumirniy

unread,
Sep 21, 2018, 5:52:40 AM9/21/18
to golang-nuts
This is something that I have seriously considered to be the right way for Go to express other generic types. I am in fact in the middle of a project at the moment where I have created a pair of generic byte buffer types, one is the regular kind, the other uses memguard locked buffers, for keeping secrets out of reach of other processes on a system.

A lot of the complaints that lead to the idea of creating a generics system in Go tend to start from the absence of a few basic types. One is the set, and in particular the union operator. However, it is trivial to implement various types of sets and set operations based on maps or slices, and more often than not you save absolutely no time predeclaring a framework when you can just embed a number of other structs inside a type as well as by implementing or composing in interface implementations.

But knowing from having done quite a bit of study of data structures, being one of my favourite subjects, that one-size-fits-all only really happens in a very small number of cases. Hash table indexes like used to make maps, arrays and slices, but binary trees, since someone mentioned it, these have many different forms, as dictated by their application. You have heaps and you have trees, the former are better for queues and the latter for optimising searches. There is several ways to implement them. You have B-trees, btrees, red/black, B-heaps, AVL, and so on and so on. These are not really subject matter for basic language types, though the language might improve their use and readability with a number of special keywords and declaration types.

I think that the solutions to most of the problems lies in embedding error status into every type (except maybe machine-sized integers), a conditional return statement that allows you you to handle errors concisely within a statement block, and possibly some kind of built-in addition, in addition to the universal error status flag, which integrates some kind of configurable serialisation function for wire and storage. There is many options already available, but to make it simpler to invoke 'freeze' and 'thaw' functions, which provides a data format that can optionally contain type metadata and enable this data to become, no matter what its' type, an object that any other function can pass around and as required see and access parts of the data that relate to the interface.

Very small changes, is what I am suggesting. Implicit error type in all but integers, a conditional break/return function (probably one for each) and more generally what I am suggesting is extend the built in 'error' interface to add set, unset and serialise/deserialise functions. And maybe a built-in structured logger that lets you track the state of all variables as each function operates on it, but only automatically in case of throwing an error.

I think that go almost already perfectly implements the generic type []byte. Everything can be turned into []byte and []byte can encode any other variable.

Go is absolutely a leader in the programming language field. I just saw yesterday a blog post about 'new features in C++17' or whatever, 'that simplify your code'. Every single feature already in Go, the optional first statement in the 'if' structure, type inference through assignment to constants, and somethting else I forget. In every area of the core language, Go uses a pattern that is both simpler and more powerful than you find in most other languages, and that can trivially express many other patterns usually in a far more concise yet readable manner.

The changes for version 2, in my opinion, should first identify the real meat of the issues that people point at their previous language experiences to talk about, but go far deeper to decisions about syntax that are in many cases now quite obsolete concerns. In the old days, every byte was so expensive. The price is very low now and basically seems to be flattening out, so people are slowly starting to realise that certain conventions were only based on this bit-conservation.

Another issue has to do with creating brittle codebases with excessive need for copy and paste to implement something where in each instance the code only differs by the type of only one variable, that is functionally the same only differing in such as the number of bytes it can encode, sign, or any arbitrary number of other groupings of elements. Go's interfaces and embedding go a long way to neaten this up but this really is at the centre of what needs to be improved, and before anyone actually changes the language, explore the ways of expressing the pattern in go to see if the solution might just be idiom rather than syntax. Most of Go's idiom has been quite certain and settled and you don't take long to see by violating it what extra work and complexity you create. I have pretty high confidence in the Go Authors rejecting all but the simplest, and most expressive solutions, and favouring the codification of idiom when just using the right pattern gives all the benefits that extra complexity of syntax is being proposed for.

Lucio

unread,
Sep 21, 2018, 4:15:19 PM9/21/18
to golang-nuts


On Friday, 21 September 2018 11:52:40 UTC+2, Louki Sumirniy wrote:
This is something that I have seriously considered to be the right way for Go to express other generic types. I am in fact in the middle of a project at the moment where I have created a pair of generic byte buffer types, one is the regular kind, the other uses memguard locked buffers, for keeping secrets out of reach of other processes on a system.


I like this exposition and I think it has inspired me to a realisation I have not seen mentioned in the many discussions I have tried to follow, namely that the existing generics in Go - numeric operators and  few special-purpose intrinsic functions, have a common property: only a very few operands, something that user generics will no doubt not be restricted to.

In other words, there is an overarching "contract" involved that enforces "simplicity". I don't know what the designers of a generic polymorphism paradigm can make of that, but I'm sure that much of the blood, sweat and tears shed so far have been caused in an attempt to extend the complexity  of "generics" in addition to the scope of the receiver, arguments and result lists.

I hope this is helpful.

The other thought I had, but is very poorly baked, is that the "essence" of polymorphism in Go operators is analogous to the informal definition of interfaces: it can be determined by the compiler, even though it is not expressed in a formal notation (the magic of super-types like for example "numerics" to which the addition operator applies).

In a nutshell, defining a polymorphic function would entail omitting explicit type qualifications entirely and allowing the compiler to determine compatibility from the use of polymorphic operators and consequently rejecting combinations that, like in interfaces, do not fit permissible combinations of such operators. Lack of implicit conversions is then the remaining problem and I have not found (or even sought) a solution to that, but others' vision will no doubt be further reaching than my own.

I'm sorry if I'm not expressing my ideas very clearly, this is stretching my understanding somewhat.

Michael Jones

unread,
Sep 21, 2018, 6:13:47 PM9/21/18
to Lucio, golang-nuts
these seem excellent points all around.

one area of difficulty seems to me the lack of operator overloading. now, hold your breath, i'm not arguing for it here. but it highlights a kind of structural issue that is in the air around the discussion of generics. consider:

i instantiate a tree type to handle uin64 data. under the proposal, this should work because guards assert that the type proposed must allow "==" to be used.

i instantiate with some custom type that needs its own custom "isEqual(a,b)" -- that will not instantiate because there is no "==" operator for my custom type, even though there is an equality tester but its name is not "==" and i can't overload "Operator==" to say so.

this is not an argument for operator overloading but rather the observation that what the generic tree code actually wants is not an "==" for instantiated types but the more general and less specific notion of "what can i call to determine equivalence?"

one way is to have operator overloading so my type has equivalence testing under a standard name. (==)

one way is to instantiate with the type name and a "test for equal" function pointer that when nil means "just use =="

one way is to annotate my type's "isEqual()" a la json annotation with a hint saying "use this for equality testing"

there may be many ways. but it seems generics would be simpler and more generic if there was a way to be clear about such things. that is, to be generic we want to ignore inessential differences in instantiation, but extraneous difference (the name of equality testing) makes the essential and inessential more confused.

--
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.

Lucio De Re

unread,
Sep 22, 2018, 1:23:51 AM9/22/18
to Michael Jones, golang-nuts
On 9/22/18, Michael Jones <michae...@gmail.com> wrote:
> these seem excellent points all around.
>
> one area of difficulty seems to me the lack of operator overloading. now,
> hold your breath, i'm not arguing for it here. but it highlights a kind of
> structural issue that is in the air around the discussion of generics.
> consider:
>
> i instantiate a tree type to handle uin64 data. under the proposal, this
> should work because guards assert that the type proposed must allow "==" to
> be used.
>
> i instantiate with some custom type that needs its own custom
> "isEqual(a,b)" -- that will not instantiate because there is no "=="
> operator for my custom type, even though there is an equality tester but
> its name is not "==" and i can't overload "Operator==" to say so.
>
>
> this is not an argument for operator overloading but rather the observation
> that what the generic tree code actually wants is not an "==" for
> instantiated types but the more general and less specific notion of "what
> can i call to determine equivalence?"
>
> one way is to have operator overloading so my type has equivalence testing
> under a standard name. (==)
>
That's the APL approach, before operator overloading was invented. It
*nearly* got there, but the concept of "overloading" wasn't
sufficiently evolved yet. Naturally, there are other issues involved
there as well.

> one way is to instantiate with the type name and a "test for equal"
> function pointer that when nil means "just use =="
>
That's an interesting idea that makes a lot of sense in the Go 1
context, but it is a bit of compromise to convention that may have
shortcomings. Or maybe the exact opposite, it may well be the broadest
possible application.

> one way is to annotate my type's "isEqual()" a la json annotation with a
> hint saying "use this for equality testing"
>
This has been my thought for a while: a type is nothing more than a
variable attribute, which in some sense makes a property such as
"testable for equality" just such an analogous attribute for a type.
In fact, then pointers and indexing can also be reduced to analogous
properties and in due course one arrives at "mutable types" and a
whole new way to look at the compiler's job.

I know that's a stretch, but even if Go 2 does not provide for the
full extent of such a step, applying "attributes" to a language's
elements does open opportunities and in a disciplined environment
(which is where Go has the lead, in my opinion), a lot can be achieved
that has been much harder until now (sorry, I can't help being a bit
immodest about these "insights").

>
> there may be many ways. but it seems generics would be simpler and more
> generic if there was a way to be clear about such things. that is, to be
> generic we want to ignore inessential differences in instantiation, but
> extraneous difference (the name of equality testing) makes the essential
> and inessential more confused.
>
I think we've "overloaded" types for a long time without consideration
for the "essence" of a "type". My approach suggests that some
attributes that we use to qualify language elements do not fall
squarely under "type attributes", but need a few additional categories
and the discipline that only formalising them can produce.

Examples would be "immutable", "units" (the Go "time" type is a case
in point) and of course indirection and indexing. There probably are
others, as yet undiscovered, as much as pointers were a novel idea
back in the 1960s.

Once we break out of that box, we may desperately need to find ways to
control that genie, though :-).

Lucio.

Lucio De Re

unread,
Sep 22, 2018, 2:53:14 AM9/22/18
to Ian Denhardt, golang-nuts
"Google ate my homework, sir!"

Let me try again...

On 9/22/18, Ian Denhardt <i...@zenhack.net> wrote:
>
> This is a very good insight. There's no technical reason why Go couldn't
> define a built-in interface, much like `error`, for various operators,
> e.g.
>
> type adder(type T) interface {
> Add(T) T
> }
>
> ..and then the `x + y` just becomes a shorthand for `x.Add(y)`. Note
> that to express this we need the generics, because we need e.g. `int` to
> implement `adder(int)`, while `uint8` implements `adder(uint8)`.
>
This seems itself a very neat approach, in the light of my own intuition.

> Michael Jones's reply articulates a concern that I don't see a clean way
> around other than something like operator overloading: the current
> proposal leaves us with redundant interfaces; you have two ways of
> talking about equality, one for basic types and one for user-defined
> types. Same thing for comparison (people will want to sort both ints and
> user-defined types), and many of the other standard operators.
>
> It seems like the proposal would be massively simplified by operator
> overloading. Many people seem to have a general aversion to the idea,
> but I haven't yet seen the problem articulated in a way that makes sense
> to me. Why is it okay for `Write` to do anything from putting bits on a
> disk, to sending them around the world, to adding them to the state of a
> cryptographic hash function, but not okay for `==` to express equality
> on anything but a small handful of types?
>
> I have a suspicion that for many people (not necessarily), part of the
> concern comes from bad experiences with the way C++ does overloading.
>
The concern I have heard voiced (and agreed with) since the first
release of C++ is that operator overloading creates too great an
opportunity for obfuscation and some may find it irresistible, not
only intentionally, but with malicious intent.

It is a civilising feature of human nature not to release weapons of
mass destruction upon an unsuspecting audience. One also does lose
opportunities in the process and we may be on a cusp right now in
exactly this way.

> One problem is that it allows you to overload essentially *any*
> operator, including things like `&&` and `||` whose short-circuting
> semantics can't be replicated by a method. Thinking of `+` as a method
> call on the other hand is an abstraction that doesn't leak.
>
> I would like to hear a clear argument as to what is actually wrong with
> operator overloading, as it isn't obvious to me and it seems like folks
> are discarding a solution that has the potential to substantially
> simplify the proposal, without a well-articulated reason.
>
Operator overloading, perhaps even a more disciplined equivalent, does
not seem to be sufficient to encompass the entirety of the generics
problem, but it helps to identify the need for an extension to the
"type" concept that the C++ "class" does not define adequately, but
almost certainly aspires to.

I think the approach by Ian above is a step in the right direction: a
formal description not just of a "type" concept, but of practically
any language element in Go that permits us to add attributes to
objects that are not yet covered by existing notations. That is Go's
contribution to programming notations, a disciplined approach such as
would be essential to discourage, not prevent, the obfuscation made
possible by "operator overloading".

In a way, after all, generics are exactly that: each a unique
operation applied to different "type instances" (in defined
combinations) that are expected to have specifically *that operation*
in common. Or, more appropriately, *that operation* is conventionally
believed to serve analogous purposes in different, but also similar,
"namespace" described by their parameters.

In seeking to formalise the mechanism to declare the common
properties, we also identify the differences, such identification
being itself a formalisation. That is the uncharted territory we can
no longer ignore and Go is a door opening into that territory. I don't
think what will be discovered there will leave Go unscathed: I think
there will have to be incompatible adjustments, I'm not sure that the
Go developers have a fall-back plan when their stated intention to
retain backwards compatibility with Go 1 is proven (if it is) to be
impossible to attain.

But the core essence of Go is its simplicity. As long as it remains an
objective and everything possible is done to retain such simplicity as
far as possible, Go will remain the pioneer in its field.

On personal note, at this point there has been a lot of talk around
generics, one may well be led to believe that Go 2 is "just Go with
generics" (error handling in Go 1 is quite good enough for me, I only
gave a cursory glance to the proposal on enhancing that aspect of Go -
nothing else has caught my attention), which does not do the language
and its inventors and developers justice.

I feel Go 1 missed an opportunity to assimilate the principles
espoused by E.J. Dijkstra, in return for which they have provided the
eminently pragmatic language we can actually use without being
mathematically minded. I am hoping a second generation Go, or maybe
even a fork of it, will find more in "The Discipline Of Programming"
to incorporate. It is not the Gospel, it was at the time mere prophesy
:-). Today, we can put it to the test and also improve on it even
further than Go did ten years ago.

Lucio.

Louki Sumirniy

unread,
Sep 22, 2018, 3:22:08 AM9/22/18
to golang-nuts
In my opinion the interface represents the simplest way to allow operator overloading. With an inbuilt interface that covers all the symbols and some sort of category to break them down (boolean logic, binary logic, arithmetic, misc unary, prefix infix and postfix). Then you can have the built in interfaces for 'countable', 'comparable', etc etc.

The thing that is the sticking point for me is that when I look through c++ code very often all of these things are implemented in a type but never used, only constructors and sometimes destructors. I would think that creating a special named function for classes, like a built in interface, for initialisation and cleanup would be useful, something that triggers at the end of scope unless the pointer is owned by another name somewhere, and satisfy a lot of these issues. As for being able to take a big.Int and + it with another thing, honestly, the amount of code you have to write with these types is really so small the benefit probably doesn't even pay off unless you are doing a LOT of big number math in a long algorithm, for the cost in declaring the operators compared to just using the function notation.

Ian Denhardt

unread,
Sep 22, 2018, 8:30:27 AM9/22/18
to Lucio, golang-nuts
Quoting Lucio (2018-09-21 16:15:19)

> The other thought I had, but is very poorly baked, is that the
> "essence" of polymorphism in Go operators is analogous to the informal
> definition of interfaces: it can be determined by the compiler, even
> though it is not expressed in a formal notation (the magic of
> super-types like for example "numerics" to which the addition operator
> applies).

This is a very good insight. There's no technical reason why Go couldn't
define a built-in interface, much like `error`, for various operators,
e.g.

type adder(type T) interface {
Add(T) T
}

..and then the `x + y` just becomes a shorthand for `x.Add(y)`. Note
that to express this we need the generics, because we need e.g. `int` to
implement `adder(int)`, while `uint8` implements `adder(uint8)`.

Michael Jones's reply articulates a concern that I don't see a clean way
around other than something like operator overloading: the current
proposal leaves us with redundant interfaces; you have two ways of
talking about equality, one for basic types and one for user-defined
types. Same thing for comparison (people will want to sort both ints and
user-defined types), and many of the other standard operators.

It seems like the proposal would be massively simplified by operator
overloading. Many people seem to have a general aversion to the idea,
but I haven't yet seen the problem articulated in a way that makes sense
to me. Why is it okay for `Write` to do anything from putting bits on a
disk, to sending them around the world, to adding them to the state of a
cryptographic hash function, but not okay for `==` to express equality
on anything but a small handful of types?

I have a suspicion that for many people (not necessarily), part of the
concern comes from bad experiences with the way C++ does overloading.

Louki Sumirniy

unread,
Sep 22, 2018, 10:47:00 AM9/22/18
to golang-nuts
I think the thing everyone who likes operator overloading like mainly is being able to do infix and postfix syntax, instead of only prefix (function). But then also what do you do about interfaces that also implement an operator interface? I'd guess biggest reason to not do it is 

1) no human readable distinction between actual operations and one has to decompose the code quite a lot as both types have to be known before you can attach it to an interface

2) there is very few cases where being able to use infix operators makes that much difference to readability, it's like you want some mathematical notation but it's still all in lines. Chaining pass-through methods works just as well and the types are much easier to identify before you even fully parse it.

Lucio

unread,
Sep 22, 2018, 11:46:34 AM9/22/18
to golang-nuts
On Saturday, 22 September 2018 16:47:00 UTC+2, Louki Sumirniy wrote:
I think the thing everyone who likes operator overloading like mainly is being able to do infix and postfix syntax, instead of only prefix (function).

It's good that you brought that up, because another issue I remember from C++ V1.0 days, is that operator overloading did not allow for changes in operator precedence, an arbitrary sop to some weird decisions taken in earlier centuries. What I see as pertinent here, is that precedence is yet another "type" property, this time not of the arguments to an operator, but the operator itself.

As I pointed out in private correspondence to Ian Taylor, the entire mess of arithmetic operations ought to be delegated to an APL-like interpreter and all the complexities, of which being "generic" functionality is not the only one, becomes one less problem for the Go compiler. If APL is too obscenely obscure in the Go context, no doubt there will be alternatives: it did not take Rob Pike long to produce Ivy.

Of course, we also have indexing, indirection and a few other features of Go whose role could be examined and formalised, perhaps more successfully once the obscurity contributed by the arithmetic features is expelled from the language.

Ian's response will remain with me for as long as I live, as I think it is very apt summary: that would not be Go. I entirely agree with him.
 
But then also what do you do about interfaces that also implement an operator interface? I'd guess biggest reason to not do it is 

1) no human readable distinction between actual operations and one has to decompose the code quite a lot as both types have to be known before you can attach it to an interface

2) there is very few cases where being able to use infix operators makes that much difference to readability, it's like you want some mathematical notation but it's still all in lines. Chaining pass-through methods works just as well and the types are much easier to identify before you even fully parse it.

Once we treat mathematical expressions as orthogonal to the language, we get brand new choices, possibly opportunities: what is Boolean? What is a string that is not an array? Etc. A whole new chapter opens, for better or for worse.

Lucio.

Michael Jones

unread,
Sep 22, 2018, 1:15:00 PM9/22/18
to Lucio, golang-nuts
the reason i wrote something like "...operator overloading, but wait, don't get excited..." was to bring awareness of a core problem without (hopefully) having people bring the burden of experience. i say burden because bad experiences can shadow the 'why' that was good with the 'how' that was bad. let the why foremost to "break these chains, rise up, and move beyond" as in Callicles' famous speech.

the essential meaning of operator overloading and go interfaces and Smalltalk messaging is a way to bind code to intent. (in general intent is named uniquely ("==") for simplicity but that is independent.) Generics raise the need a way to say how the standard intentions play out for our types. Imagine our gopher attired as a waiter, politely asking questions of a custom type:

gopher: Will you want to test for equality, madam?
type: Yes, thank you.
gopher: How would you prefer that test to be done?
type: Hmm... I'll try 'IsEqualWithOddName(a, b *type)" for now.
gopher: very good, madam.

This mutual understanding needs to happen. how is open to discussion. go interfaces use standard method names. operator overloading uses standard symbols. macro expansion uses arguments. no matter how it manifests, the power of generics relies on understanding related parts. we should talk about what kinds of parts deserve awareness.

--
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.

Ian Denhardt

unread,
Sep 22, 2018, 11:53:56 PM9/22/18
to Lucio, Michael Jones, golang-nuts
On Saturday, 22 September 2018 16:47:00 UTC+2, Louki Sumirniy wrote:
>
> I think the thing everyone who likes operator overloading like mainly
> is being able to do infix and postfix syntax, instead of only prefix
> (function).

My own reason for wanting this is not really about syntax, so much as
being able to define functions etc. which e.g. check for equality,
without having to write too versions -- one that uses `==` and one
that calls some method custom types. The syntax isn't really the point;
there's an underlying notion of equality that we want to be able to talk
about for more than just built-in types. We could define an interface
for this:

// The Equatable interface wraps the basic Equals method.
//
// x.Equals(y) tests whether x and y are "the same." The predicate
// Equals should obey a few common sense rules:
//
// 1. It should be reflexive: x.Equals(x) should always return true
// (for any x).
// 2. It should be symmetric: x.Equals(y) should be the same as
// y.Equals(x)
// 3. It should be transitive: if x.Equals(y) and y.Equals(z), then
// x.Equals(z).
//
// It generally does not make sense for a type to implement
// Equatable where the type parameter T is something other than
// itself.
type Equatable(T) interface {
Equals(T) bool
}

What I am suggesting is merely that `==` desugars to a use of this
interface.

An important litmus test for any operator we consider for overloading is
whether we can come up with a clearly specified interface for it like
the above. If not, it does not make sense to allow the operator to be
overloaded, since it is not clear what overloaders should do. I believe
this is the source of most of the problems with operator overloading in
other languages.

I think if we stick to this things will stay under control; there's
currently nothing stopping folks from defining an instance of
io.Writer that does something utterly in conflict with what is described
in its documentation -- but that hasn't seemed to be a problem in
practice.

Quoting Michael Jones (2018-09-22 13:14:21)
> the reason i wrote something like "...operator overloading, but wait,
> don't get excited..." was to bring awareness of a core problem without
> (hopefully) having people bring the burden of experience. i say burden
> because bad experiences can shadow the 'why' that was good with the
> 'how' that was bad. let the why foremost to "break these chains, rise
> up, and move beyond" as in Callicles' famous speech.
> the essential meaning of operator overloading and go interfaces and
> Smalltalk messaging is a way to bind code to intent. (in general intent
> is named uniquely ("==") for simplicity but that is independent.)
> Generics raise the need a way to say how the standard intentions play
> out for our types. Imagine our gopher attired as a waiter, politely
> asking questions of a custom type:
>
> gopher: Will you want to test for equality, madam?
>
> type: Yes, thank you.
>
> gopher: How would you prefer that test to be done?
>
> type: Hmm... I'll try 'IsEqualWithOddName(a, b *type)" for now.
>
> gopher: very good, madam.
>
> This mutual understanding needs to happen. how is open to discussion.
> go interfaces use standard method names. operator overloading uses
> standard symbols. macro expansion uses arguments. no matter how it
> manifests, the power of generics relies on understanding related parts.
> we should talk about what kinds of parts deserve awareness.
>
> send an email to [2]golang-nuts...@googlegroups.com.
> For more options, visit [3]https://groups.google.com/d/optout.
>
> --
>
> Michael T. Jones
> [4]michae...@gmail.com
>
> --
> 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 [5]golang-nuts...@googlegroups.com.
> For more options, visit [6]https://groups.google.com/d/optout.
>
> Verweise
>
> 1. mailto:lucio...@gmail.com
> 2. mailto:golang-nuts...@googlegroups.com
> 3. https://groups.google.com/d/optout
> 4. mailto:michae...@gmail.com
> 5. mailto:golang-nuts...@googlegroups.com
> 6. https://groups.google.com/d/optout

Robert Engels

unread,
Sep 23, 2018, 2:35:15 AM9/23/18
to Ian Denhardt, Lucio, Michael Jones, golang-nuts
Issues like these highlight the deficiencies of Go compared to Java. The Java designers understood languages far better, and from the start realized that identity and reference equality were different concepts. Everyone in Go land are debating these solved issues. Pick and chose what you want to implement but there doesn’t really need to be a debate on how to do it.

Sent from my iPhone
> 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.

Louki Sumirniy

unread,
Sep 23, 2018, 8:01:28 AM9/23/18
to golang-nuts
The thing that people are concerned about is creating a construct that enables you to write legal but confusing and unclear code. Let's say we steal triple equals ===, and then use it to mean some kind of special assignment operation. Or we make = become an addition operator infix for a string-related type.

Interfaces don't cover the case of operator precedence, interfaces are all verb subject object. Operator precedence is a syntactic layer that does not have at least an easily inferred type and require deeper analysis of the AST before you can get the types right. The bigger reason to not allow constructs like this in Go is because one of its main advantages is simple parsing, which means tooling and nearly interactive speed testing. Go even can use spaces (or not) to override precedence. Either the implementation forces each operator symbol to be of only one precedence relation to others, or you are leaving out milk and cookies for the kinds of programmers that make people want to invent a language like Go.

The case for equality operators is a pretty good one though. The reason why an infix operator is preferable for equality operations is because both operands are being tested for equality. It doesn't matter which order, unlike verb subject object of function notation. + is already used for string concatenation, logically it could and probably should also apply to any type of sliceable array type.

I am against a wide open operator override system, most especially one that allows the disruption of the conventional order of precedence for the operator. But a small set that is declared like an interface and only covers a very small subset, the most useful ones, like +, ++, --, !=, ==, <, >.

A central pillar of the design process of Go is all about avoiding complexity where flexibility can substitute and not add an excessive and questionable cost for an operation that is not that commonly used especially when it decreases the approachability of code. Overloaded equality, increment and comparision operators would not increase the ambiguity for either humans or the source code. If you can assert the type of an operator from only one of its operands this is less parsing than a case where you have to disambiguate both. Type casting should never be implicit, so for a Go version of operator overloading, there would not be conversion involved - a full parse showing differing types on an operator operand would thus be illegal. Unless both operands are both implementors of the same countable interface, for example, and they either use interface{} or a defined interface name as returns/inputs. I think that this is a step further on from just allowing redefinition of (some) operators to bind to new types, and to allow those types to create assignment channels, for example. And this is where the big syntax bugbear appears. Overriding an operator is one thing, but defining two types as being comparable and assignable with each other will pretty quickly grow into a hella boilerplate for a shortcut used 10 times in a 1000 line project, and ever growing compilation times.

Go is a simple language, and anything that will take away the simplicity, with its benefits in learning, reading and compiling, will and should be rejected as an addition to the language spec. I would argue that a small set of infix operators and maybe some unarys would not create such complexity. Especially if we restrict it to unary post/prefix, addition, subtraction equality and assignment, and don't change the precedence rules. Really, precedence would not be relevant, and there should not be any ambiguity allowed in how an overridden symbol can be used. Precedence really only irritates everyone between add/subtract and multiply, square, and only for countable types. To stop complexity creeping into the parser, also, no change in the rule about casting. No implicit casts. Implicit casts are probably one of the most irritating and pervasive features of C type languages. I always have to double and triple check. Even between some version changes C++ has substantially disrupted the rules about it and I just don't think that implicit casting is a good construct in a language. Implicit typing through inference does not have this problem.

Robert Engels

unread,
Sep 23, 2018, 11:59:29 AM9/23/18
to Louki Sumirniy, golang-nuts
I’m sorry. I did not mean to offend anyone. It came out wrong and I apologize. 

Sent from my iPhone

Lucio De Re

unread,
Sep 23, 2018, 12:02:14 PM9/23/18
to Robert Engels, Ian Denhardt, Michael Jones, golang-nuts
I take exception to that statement, your notion of "understood
languages much better" doesn't parse in light of the fact that you are
here, debating the merits of Java in the primary Go forum instead of
writing wonderful code using the language you respect so much.

That's either hypocritical or merely absurd in addition to the rather
inappropriate, unjustified and offensive ad hominem that accompanies
it.

Lucio.
--
Lucio De Re
2 Piet Retief St
Kestell (Eastern Free State)
9860 South Africa

Ph.: +27 58 653 1433
Cell: +27 83 251 5824
FAX: +27 58 653 1435

Robert Engels

unread,
Sep 23, 2018, 12:18:25 PM9/23/18
to Lucio De Re, Ian Denhardt, Michael Jones, golang-nuts
I take offense to that. I apologized for my statement that was worded more harshly than intended. But if you think that Go is beyond criticism just because of ??? Anything??? Go is a GREAT tool for many classes of applications, but it is certainly not appropriate for all use cases. Maybe with open criticism it could get there, but I don’t think your attitude will help it. I am certainly not the first nor the last to highlight many deficiencies in Gos design. That’s the great thing about software though, it is malleable.

Sent from my iPhone

Lucio De Re

unread,
Sep 23, 2018, 1:13:29 PM9/23/18
to Robert Engels, Ian Denhardt, Michael Jones, golang-nuts
On 9/23/18, Robert Engels <ren...@ix.netcom.com> wrote:
> I take offense to that. I apologized for my statement that was worded more
> harshly than intended. But if you think that Go is beyond criticism just
> because of ??? Anything??? Go is a GREAT tool for many classes of
> applications, but it is certainly not appropriate for all use cases. Maybe
> with open criticism it could get there, but I don’t think your attitude will
> help it. I am certainly not the first nor the last to highlight many
> deficiencies in Gos design. That’s the great thing about software though, it
> is malleable.
>
I responded harshly, too and that wasn't necessary, so I owe everyone
an apology.

But setting Java as the paragon for Go is not going to win anyone to
your cause, either. Java exists, it is available, it is what it is.
Why would Go want to resemble it?

Lucio.

Robert Engels

unread,
Sep 23, 2018, 1:38:36 PM9/23/18
to Lucio De Re, Ian Denhardt, Michael Jones, golang-nuts
I wasn’t suggesting that Go should resemble Java. I was just trying to point out that many of the current issues under debate for Go2 have been resolved quite well in other languages, and looking to them for direction should not be out of bounds just because they are not Go. That’s a little short sighted IMO.

Ian Denhardt

unread,
Sep 23, 2018, 2:05:41 PM9/23/18
to Lucio De Re, Robert Engels, Michael Jones, golang-nuts
Quoting Robert Engels (2018-09-23 13:38:04)
> I wasn’t suggesting that Go should resemble Java. I was just trying to point out that many of the current issues under debate for Go2 have been resolved quite well in other languages, and looking to them for direction should not be out of bounds just because they are not Go. That’s a little short sighted IMO.

Java doesn't allow basic types to be used in generics at all (except for
arrays, which are magic like Go's slices and maps are today). The issue
under discussion crops up because we want to be able to talk about both
MyGenericType(MyCustomType) and MyGenericType(int). The fact that basic
types are totally separated from objects is a big wart in the language,
so Java isn't really a great source of ideas re: how to deal with the
question currently under discussion.

Michael Jones

unread,
Sep 23, 2018, 4:34:50 PM9/23/18
to Robert Engels, Lucio, i...@zenhack.net, golang-nuts
You did not offend me. This is a place for earnest ideas and all are welcome.

Your comment is not yet persuasive, but it might become so. Still thinking. I first heard from James Gosling about Java when Java was Oak. He seemed proud to say, "C++ has objects. Well, I'll show them, I'll make everything objects!" That they did. I've been recalling those days and wondering if that team knew more about language design in some privileged way or was just turning the knob to eleven. Still not sure.

I am sure that Alan Kay et al had important, fundamental insights in Smalltalk. Variables had named attributes and they could be inspected in code and visually with an inspector. I could ask you about your type, size, length, etc. If i asked an inapplicable question i got a survivable answer, not a panic. I raised this pre-Go1 in Google with the idea of, why are Len() and Cap() not implicit methods on a slice? I like that because it also suggests asking a variable, "hey, what is your minimum expressible value?" in a way that is type agnostic. Being able to as parameter t of generic type T a question like "x := t.MinExpressibleValue()" has a grand flexibility compared to a type switch that then knows about library functions for math.MinFloat64(). There were reasons why not, but in this quest I'm not persuaded against the notion that every common attribute of T should be knowable from t, an instance of that type. In Go this happens with magic compiler generics (len(x), cap(x), ...) that take any and all types and do the right thing. I'm more a fan of no magic and a simple universal notion that applies to every type, built-in or custom.

In this sense, the Java team's gusto and my deep respect for Smalltalk's "a queryable database of instances" (aka, DOM decades before) align and I do like that path.

Nhiên Phan

unread,
Sep 23, 2018, 5:30:01 PM9/23/18
to i...@zenhack.net, lucio...@gmail.com, ren...@ix.netcom.com, michae...@gmail.com, golan...@googlegroups.com
Hi everybody. I'm a beginner at Golang. Anyone who has the talent to Golang please let me please

robert engels

unread,
Sep 23, 2018, 10:51:17 PM9/23/18
to Michael Jones, Lucio, Ian Denhardt, golang-nuts
I’m often confounded when people discuss Java (at least in comparison to Go) as being “heavy”. If you read early interviews with Gosling it is clear that his design was more about what to leave out, not what to include (macros, pre-processor, unsigned arithmetic, etc.) He is a brilliant language designer, probably top 10 CS engineers, and I think he’s worth listening to. I think as people look to “enhance/improve” Go, his thoughts and processes might be of value.


The role of simplicity

Venners:The opposite of complexity is simplicity. I have often heard you describe your philosophy when designing Java in the early days: you didn't put something in Java unless five people screamed at you and demanded it. In one interview, you told this really good story about moving to a new apartment and something about keeping things in boxes.

Gosling:That's actually a general principle for life that works really well. When you move to a new apartment, don't unpack. Just sort of move in, and as you need things, pull them out of the boxes. After you've been in the apartment for a couple of months, take the boxes -- don't even open them -- and just leave what's in there and throw them out.

Venners:The 'don't even open them' part is important because it's very hard to throw things away once you know what they are.

Gosling: Right, because if you open them, you say, 'oh, I can't part with that.'

Venners:So would you say that simplicity is a general philosophy programmers should always have when designing programs?

Gosling:I think in any kind of design, you must drive for simplicity all the time. If you don't, complexity will nail you. Dealing with complexity is hard enough.

In programming language design, one of the standard problems is that the language grows so complex that nobody can understand it. One of the little experiments I tried was asking people about the rules for unsigned arithmetic in C. It turns out nobody understands how unsigned arithmetic in C works. There are a few obvious things that people understand, but many people don't understand it.

So one of the most important criteria for judging a design for me is the manual. Is the manual out of control, or is it reasonably concise? You can write a pretty decent Java manual in less than 100 pages. The current Java language spec is pretty thick, but that's because it's probably the most detailed language spec ever written. It goes through all of the details. I couldn't write the Java language spec.

Louki Sumirniy

unread,
Sep 24, 2018, 12:01:53 AM9/24/18
to golang-nuts
Choosing a virtual machine target was the wrong decision. All machines have a machine in them, why add a virtual one? C already could be compiled on every platform, all we got out of it was processing latency and a whole extra layer of performance wrinkles that the CPU maker probably already fixed.

It's my opinion that if you really think that Go is missing generics you just don't know the language well enough nor have used it to solve problems others used parametric polymorphism or generics.

The clamour for solutions in the area of error handling and (easier) generic programming for go has to do with the wordy constructions required to implement data abstraction. To speak of java, it also has the interface, and was the biggest champion of the concept for a long time. 

But it's not the feature everyone talks about Java. That's the JVM on every processor and the OOP constructions, as everywhere else in programming there is no interface. 

But interfaces can solve a lot of these problems and cut down on boilerplate, and Go's stdlib already has loads of good though unsophisticated examples of interface programming. 

Also in this discussion people are talking about how languages like Smalltalk have deep reflection capabilities. Go has 'reflect'. and go has interfaces. You can often dodge a lot of use of reflect by using interfaces, and *making* types with more metadata is not difficult with Go, at all. The error inbuilt interface, for example. It is extremely primitive. The way it should be used is that you declare (almost) all types as structs and add the attributes you want to them and create interfaces that pass and process this data. Then you can use the dot notation to form ad hoc constructors, you can create second level names using structs, and the thing I have discovered is that once you have a really good set of interfaces for things like coding modes, error handling, and so on, when you go to actually write the application you can say a lot with quite a little.

The downside of this complexity-averse philosophy in go is that even though the concept is not that young, I think there is some ways to go before the best patterns for interfaces are a settled question. What I love most about interfaces is they let my code start to look more like poetry and less like math. Maintaining code is very important, and though in some areas Go can be quite wordy, the constructions are quite naive and are not difficult to edit en masse, the invention of multiple cursors in editors is something Go programmers can greatly benefit from. Repetition is bad if it says nothing new but at the same time it is easier to unfold what it contains when it is in a simple syntax, and easier to generate automatically. Code generators are another thing I have started looking at, and I know there is a lot in there that would make this type of programming simpler. Turning a struct into JSON that can be unmarshalled using reflect does not require reflect, a simple string concatenation with some string conversions, which is just a bit of array shuffling. The same applies to database serialization. In go many times you can gain a lot with the use of some of the wordy stuff, and then afterwards you have a neat little black box that you can trust, and never have to use that wordy stuff unless you are just building something new without a clear map of its parts.

Go puts a lot more power in the hands of the programmer, and doesn't sugar over very many things at all. Whether it reads well and is comprehensible is more dependent on the programmer than the language, but the benefit is being able to ride so close to the metal whenever you need to, and a neat construction system to give more flexibility and power.

It's my opinion that when you start to try using interfaces and goroutines in new ways you have less bad to say about what is sacrificed for this power. I kinda basically wasn't programming much during the time of the rise of OOP though it seemed cool when it came out, I was just used to BASIC and assembler and my favourite language back then was called "E", which, similar to Go, combined a very simple syntax, mashed together some syntax patterns from C and Pascal style, but man that compiler flew, it felt like nearly as fast as assembler.

Keeping the compilation process simple does mean the programmer must adapt their thinking to be more like the machine, but on the flip side, you can run through files and make multiple changes easily, with no ambiguity of symbols, search and replace and multiple cursors, quite quickly, and immediately see the change, plus, more easily write tools that parse it. There is many tools already, but the maturity of the field of automated programming isn't mature yet, in my view. Instead of wanting go to sacrifice that speed and simplicity so you can read your code easier, you will just use some declarative focused, maybe mixed embeds of code, add the ability to know which is which with the editor to parse it live in the background and you'll be scratching your head wondering why you wanted to sacrifice the benefit of fast and deterministic compilation at all. Look at what is happening in the world of Javascript... 90% of the code these days is running as JS, but probably 80% of it is generated by javascript translators, and how declarative they are. Nothing is stopping the use of this. Either in code generators or in frameworks that process these descriptions during runtime.

Go makes all these things even easier than javascript, has nearly as much expressive power alone as java, and we are only at 11 years since the first version. Java and Javascript most especially show that tooling is more important than the language itself for adoption, most especially javascript. Go has the potential to be a way to distribute software purely as source code and have it always run as native binary. It's hard limitations also make it very safe. So it is just the absence of these tools being built that is holding it back. Too often people are more busy coding to order and not coding to develop the toolkit. Go needs more tools. Layers that allow patchworking of declarative systems that bone out a pattern and then give you all the slots where you need to put the non-structural stuff.

>>>>  6. https://groups.google.com/d/optout
>>>
>>> --
>>> 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.
>>
>>
>
>
> --
> Lucio De Re
> 2 Piet Retief St
> Kestell (Eastern Free State)
> 9860 South Africa
>
> Ph.: +27 58 653 1433
> Cell: +27 83 251 5824
> FAX: +27 58 653 1435

Ian Denhardt

unread,
Sep 24, 2018, 12:45:21 AM9/24/18
to Michael Jones, robert engels, Lucio, golang-nuts
Quoting robert engels (2018-09-23 22:50:54)

> I'm often confounded when people discuss Java (at least in comparison
> to Go) as being "heavy". If you read early interviews with Gosling it
> is clear that his design was more about what to leave out, not what to
> include (macros, pre-processor, unsigned arithmetic, etc.) He is a
> brilliant language designer, probably top 10 CS engineers, and I think
> he's worth listening to. I think as people look to "enhance/improve"
> Go, his thoughts and processes might be of value.

I feel like a lot of the complaints about java being "heavy" comes down
to sheer verbosity, some of which is an artifact of the language itself
(no type inference whatsoever), and some of which is the way APIs tend
to be designed; you have lots of identifiers that are like six words
long, everything feels very pedantic a lot of the time. I've found java
to be painful to write without an IDE and autocompletion (like,
*physically* painful -- my bad pinky starts acting up after a while).

None of this is really about the complexity of the language per se. But
much of the discussion in this thread is about what kind of code we'll
see in the wild if we add operator overloading to Go. I think Java is a
good illustration of what that can do to a language. We could delve into
what actually went wrong there, but the point is we need to think things
through.

It seems like Gosling had the right attitude re: simplicity,
but somewhere something fell apart.

I wasn't really around for the days when folks thought Java was this
great simple thing. I started my CS bachelors' in '06; by then Java
already had generics, and they felt very bolted on. They didn't really
work with some of most commonly used types in the language (int,
bool...). Java had this mantra of "everything is an object", but it
rang hollow in light of that. So many aspects of the language seemed
slapped together even then, when I had no basis for comparison. The
subtyping rules for generics are different than the ones for arrays,
because somebody failed to think through the implications of the array
subtyping rules. This program tries to put a string in an array of
integers:

class Main {
public static void main(String[] args) {
Integer[] myInts = new Integer[10];
Object[] myObjs = myInts;
myObjs[0] = "Hello, World!";
}
}

It typechecks! and when you run it, you get:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
at Main.main(Main.java:5)

By the time generics got added, that hole in the type system had bit
people enough times that they didn't make the same mistake again. But
it's another dark corner of the language that was frustrating to have to
explain to undergrads years later when I was a TA. Not long after, the
department just started teaching them Python instead.

There are two lessons here, one about type theory and one about design.

The design lesson is that "lets keep things simple," while a great
principle, isn't a substitute for thinking through the details, and
maybe even doing the math.

The good news is that as we look at adding generics to Go, we have a
much easier problem than the designers of Java did. The interactions
between subtyping and parametric polymorphism are notoriously subtle and
complex, and it's not surprising that smart people made the
aforementioned mistake. I think some of the perceived weight of Java's
generics comes from these complex interactions.

But while Java has pervasive subtyping everywhere, Go barely has
subtyping at all, and I have a hunch that the bits that look like
subtyping at first can be formalized in terms of constructs that play
much more nicely with generics.

We can do this right, and have generics in Go be simple and feel like
they belong there. But the current contract-based design is a
non-solution. It's clear to me that we should be using interfaces
instead, and that need something like operator overloading to make this
work.

I have a sketch of a design for the system in my head; hopefully by the
end of next week I'll have time to write it down and share it.

Robert Engels

unread,
Sep 24, 2018, 2:07:27 AM9/24/18
to Ian Denhardt, Michael Jones, Lucio, golang-nuts
Just an aside, Java had generic type inference since 8, and has local variable type inference in Java 9.

Personally, I hate type inference. I find myself continually needing to refer to the docs to know what something is, when type inference is not used this doesn’t happen as the code is self documenting. I think type inference is lazy. Puts the burden on the maintainer forever when it would of been a small effort up front. For things like lambdas it makes a bit more sense but barely.

Sent from my iPhone
Reply all
Reply to author
Forward
0 new messages