
Go Contracts – Draft Design - dkarbayev
https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md
======
wybiral
This is too similar to interfaces for me and seems like it would only add to
the complexity of the language, documentation, and compile time for a small
increase in generic code.

One of Go's core strengths is simplicity. I've personally witnessed both C and
Python programmers jump right into Go projects with minimal assistance.
Learning new packages is absurdly easy because the code is so simple and
documentation practically generates itself. I worry that widespread use of
this feature would negatively impact the language in that regard.

Lacking a true "generics" feature has had an influence that people tend to
overlook, which is that the Go community is less reliant on dependencies. One
of Go's proverbs is "A little copying is better than a little dependency" [0].
The ecosystem is better documented and healthier because of it, avoiding the
require("left-pad") world of the dependency abyss.

The language lends itself to being used for smaller, more concise code bases
where things are written with specific and narrowly-defined purpose. It
implicitly discourages "Architecture Astronauts" [1] and that strength
shouldn't be overlooked or sacrificed for a few new container options.

[0] [https://go-proverbs.github.io/](https://go-proverbs.github.io/)

[1] [https://www.joelonsoftware.com/2001/04/21/dont-let-
architect...](https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-
astronauts-scare-you/)

~~~
haolez
Agreed. What I don’t get is why people try to change Go all time instead of
simply using different languages for different problems. If you absolutely
need generics in a project, there are a ton of languages out there that will
fit your bill.

~~~
wybiral
Yeah, for me Go is a specialized tool mostly for dealing with IO. The entire
standard library and all of the builtin types are really designed for IO-
centric applications which is why it's a natural fit for
servers/microservices. Small, narrowly defined applications that mostly
coordinate via sockets and serialization and it does those things really well.

In those worlds you're more likely to move data between logical units in JSON
or Protobufs (both of which Go is good for) than to need proper generics so I
don't understand the push from this subset of the community.

As far as container types and data structures, look at the number of well
built storage solutions that don't seem encumbered by the lack of generics at
all like: boltdb, bleve, BadgerDB, InfluxDB, etc.

~~~
haolez
What I really like about Go is the consistency of their tooling. gofmt, godoc,
the GOPATH convention, single binary deployments. I like all of that.

Maybe we could port some of those ideas to other languages so that Go
programmers will feel at home :)

~~~
pjmlp
Static compiling, common across all OSes before it became an option alongside
dynamic linking.

Fast compile times, already being enjoyed by many with Turbo Pascal for MS-
DOS, or Object Pascal for Mac OS, among other languages with similar support
for modules.

Godoc, javadoc did it first.

Gofmt, indent was one of the first and every IDE has done it since ages.

Other languages already have those ideas.

------
JulianMorrison
It seems as a proposal to overlap interfaces a lot.

\- Interfaces gather methods. The concrete type referenced through an
interface doesn't need to know about the interface. Interfaces can be used to
decouple operations from the details of the thing being operated upon.

\- Contracts gather methods. The concrete type referenced through a contract
doesn't need to know about the contract. Contracts can be used to decouple
operations from the details of the thing being operated upon.

Disjunction: Interfaces are resolved at runtime and result in indirect calls.
Contracts are resolved at compile time and their contract-nature is lost in
object code, they just become direct calls. Oh and contracts can do operators.

It feels to me like the risk here is not more syntax but less orthogonality.
There will be two features competing to be the standard way to do the
contract-like things.

~~~
munificent
This is because interfaces and contracts are conceptually similar. They both
give you a way to abstract over a concrete implementation of some set of
operations. That in turn lets you use multiple different types that support
those operations.

However, they operate at different times. Interfaces are a runtime abstraction
and contracts are at compile time. Interfaces are dynamic and contracts are
static. This manifests itself in important ways:

* Contracts give you a way to enforce homogeneity where interfaces permit heterogeneity. Say you want a function that takes a list of objects that you can call `Foo()` on. If you define that function as accepting a list of some Fooer _interface_ , then the list you get at runtime may contain a mixture of all sorts of different types of objects, all of which implement Fooer. If your function excepts a list of a generic type implementing a Fooer _contract_ then at runtime you get a list of objects all of the _same_ type, one which supports supports Fooer.

* Because of the above, there is a runtime overhead to working with interfaces. When you pass a concrete type to something expecting in interface, that value needs to be boxed. When invoke a method on it, you need some amount of dynamic or virtual dispatch at runtime. With contracts, you can generate code at compile time that calls directly into the chosen type because you know at compile time exactly which _concrete_ type has been chosen.

So I think you're right to point out the similarities. But I think having both
and having them be similar is a good thing. It lowers the cognitive load while
giving more things users can express. I don't think the features will compete
any more than interfaces and generics compete in other languages.

There are analogues of this elsewhere. C++ basically recapitulated this same
history decades ago. Initially it only had virtual methods but no generics.
You would see collection libraries that tried to define collections of
arbitrary types using interfaces, but it never quite worked right. A list of
ints isn't really a _subclass_ of List<T> even though there is some
polymorphism going on. Templates resolved that.

Likewise, Java added generics because interfaces were not sufficiently
expressive.

Rust has traits and trait _objects_. The two features are very similar and
reuse a lot of mechanism, but also let you do different things. Traits are
basically like contracts and are instantiated and specialized at compile time
with no runtime overhead. Trait objects are like interfaces or vtables. They
give you runtime polymorphism at the cost of some performance.

~~~
ori_b
> However, they operate at different times.

This seems like it would be solvable by simply allowing interfaces to
constrain generic parameters. Something like:

    
    
        func foo(type A Comparable, B OtherIface, C /* unconstrained */)(a A, b B) C {
            ...
        }
    

You'd need to allow for type parameters on interfaces, of course:

    
    
       func lookup(type A Indexable(B), B Hashable)(set A, idx B) {
           ...
       }
    

But there's no need, IMO, to separate interfaces and contracts.

~~~
dilap
How do u do something like a graph tho, that involves two types (nodes and
edges)?

~~~
ori_b

        Graph(N,E)
    

What's the problem you see?

~~~
uryga
afaik there's no such thing as an interface for multiple types in Go; i.e. you
can have (the equivalent of) `Foo implements Comparable` but not `Bar, Baz
implement ConvertFromTo`. `Graph(N, E)` mentions two types, so you can't
really express it as an interface, at least not in current Go.

~~~
ori_b
But this is a single interface, with two parameters. It's effectively just a
function that returns an interface type.

    
    
         Graph(N, E)
    

specialized with

    
    
         Graph(int, int)
    

is roughly equivalent to:

    
    
         interface Graph_int_int

~~~
uryga
ah, that makes sense! but what about something like

    
    
        ConvertFromTo(F,T)
    

? i mean, we need a single method

    
    
        func convert(f F) T
    

but where should it go (which type should implement it)?

~~~
ori_b
The issue there has nothing to do with generics. You need overloading for what
you want (and, IMO, overloading is a bad idea). Don't write code that way.
Since conversion is rarely a generic pattern anyways, you just pass a
conversion function in.

    
    
         func processStuff(type U, V)(a U, b V, convert func(U) V){
             bleh(a, convert(b))
         }
    
         processStuff(123, "foo", strconv.Itoa)
    

Adding the parts needed to do generic conversion functions is not a great
idea. That way lies madness and SFINAE.

------
arendtio
Some people here seem to not like the similarity to interfaces, but I think,
it should be even more like interfaces. In fact, I like the interface syntax
even better than the contract syntax. And the type parameter list pollutes the
otherwise very clean syntax in my opinion.

So you might ask what I am suggesting. AFAIK there are two major problems
preventing interfaces to be used as generics:

1\. There is no way to require the methods of builtin types aka operators.

2\. Compile-time type safety is very limited

The reason why interfaces have no access to operators and other language
features is that the language is kinda rough around the edges. And I really
don't want to hurt anybody with this statement. I love the language, but I
think there are some things that aren't perfect.

For example, just imagine every array/slice would have a method (e.g.
Position(int)) that would be the equivalent of the square brackets and the
square brackets would be just some syntactic sugar like

    
    
      arrayA[0] == arrayA.Position(0)
    

Same thing for operators like +

    
    
      x := 1
      x + 2 == x.Addition(2)
    

It would eliminate a lot of cases that make contracts necessary. In essence,
it would mean that you could actually address built-in types in interfaces.

The second problem is related to the type parameter list. In my opinion, it
would be better to simply put those types on extra lines like:

    
    
      type T Generic
      func Print(s []T) {
        // function body
      }
    

I mean, there should be no problem in making them available for more than one
function and it would definitely clean up the function signature and look more
Go-like.

~~~
atombender
Agreed, though I think the syntax problem (too many parens) could be made more
readable and Go-like by emphasizing the generic aspect and making it stand out
visually -- leaving no doubt that you're in a section that's generic, and
perhaps dissuading overuse by making it noisy. Perhaps something like:

    
    
      generic func(T, U) Map(slice []T, mapper func(T) U) []U
    

For "contracts":

    
    
      type Set generic interface(T comparable) {
        Push(T)
        Pop() T
      }
    

Every time you want to refer to a generic type you'd have to use the generic
keyword, which encourages use of concrete types:

    
    
      type IntSet generic Set(int)
    

This is more or less how Modula-3 does it. Generics have dedicated syntax with
a "generic" keyword.

------
kyrra
If you poke around the files ending with .go2 here[0], you can see some
examples of contracts/generics being used. This linked review is a prototype
of the implementation for the proposal.

[0] [https://go-review.googlesource.com/c/go/+/187317](https://go-
review.googlesource.com/c/go/+/187317)

~~~
NiceGuy_Ty
This is why even the most basic form of generics would be amazing for reducing
boilerplate: [https://go-
review.googlesource.com/c/go/+/187317/3/src/go/pa...](https://go-
review.googlesource.com/c/go/+/187317/3/src/go/parser/testdata/set.go2)

------
cjslep
I haven't been following these proposals and I want generics of some kind in
Go (I've had my fill of code generation), but good God does the proposed
syntax of using parentheses for yet another thing (in addition to method
receiver, parameters, return types, logical grouping, arguments) make the
language suddenly feel cluttered and hard to read.

~~~
eternalban
And it is basically notation noise. The idea is to inform the compiler as to
which type names are generic.

    
    
       // proposed
       func Print2(type T1, T2)(s1 []T1, s2 []T2) { ... }
    

vs

    
    
       // less noise
       func Print2(s1 []'T1, s2 []'T2) { ... }
    

This proposal does -not- seem to be informed by the same 'outer space' mindset
that gave us the Go language. A reminder that mere capitalization in Go
language informs visibility and access.

Beyond mere syntax issues, the semantics of generics itself is adding huge
complexity to what is a very capable and accessible 80% language.

~~~
Insanity
I really don't think generics (or contracts) will benefit the language. Most
people who miss it are just missing the collection frameworks from Java/C#,
and there are solutions to this anyway. (go:generate being one of them if you
really need collections for X types).

I did miss generics when I first started writing Go, but after doing so for
about a year at work and 2 years before that, I don't find me missing them at
all.

~~~
JyB
I have the exact same feeling. I was using generics extensively in C#
particularly. I don't miss them at all after having spent a lot of time
working with and understanding Go. I can't help but feel it will just make the
language far less attractive in the long run.

~~~
Insanity
Yeah I believe so as well. All languages having the same features would also
be a downside. Just make a choice and stand by it. (Like Haskell, they are not
pressured into doing things just because Java does so).

~~~
danieldk
_Like Haskell, they are not pressured into doing things just because Java does
so_

I do not mean this as a criticism of Haskell, but Haskell has _a lot_ of GHC-
specific language extensions:

[https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html)

I am not sure it qualifies as making a choice and standing by it.

------
mongol
It is interesting to compare the mostly negative reception here with the more
postivive over at reddit.com/r/golang [1]

Granted, that community in average probably contains more "Go fans". There was
no lack of criticism at the try proposal over there though.

1:
[https://www.reddit.com/r/golang/comments/cifdwf/the_updated_...](https://www.reddit.com/r/golang/comments/cifdwf/the_updated_and_simplified_draft_design_for_go_2?sort=confidence)

------
drej
I read through this yesterday and I'm fairly confused about all that.

1\. This is a long proposal. Excluding the implementation/issues/comparison
parts, it clocks in at 7700 words. Compared to the 27k-word long Go spec, this
is epic. I know the proposal is more verbose than the eventual spec (if
accepted), but still... 2\. There are some valid points in Nate Finch's
criticism of the previous proposal ([https://npf.io/2018/09/go2-contracts-go-
too-far/](https://npf.io/2018/09/go2-contracts-go-too-far/)). Be it
philosophical arguments or just plain syntax ones - like writing `func
(...)(...)(...)` in function declarations.

I don't know. I'd like some form of generics in the language, but I'm not sure
if complex designs like this are likely to be embraced by the community.

~~~
Cthulhu_
I think the complexity / length is a good indication as to why there's
resistance to adding generics to Go; frequently, Java generics are cited since
(and this is from the top of my head) it almost doubled the size of both the
Java spec and its implementations (compiler, runtime). That is a lot of
complexity to manage, and runs counter to what I believe is the current
mindset of the Go language.

And in Java it didn't stop there; the people (person?) behind generics moved
on to create Scala, a language with even more features and a notoriously
complicated 25-step compilation process (see
[https://typelevel.org/scala/docs/phases.html](https://typelevel.org/scala/docs/phases.html))
(compare with Java's six phases [https://www.javatpoint.com/compiler-
phases](https://www.javatpoint.com/compiler-phases) and Go's... three? Not
sure, I found [https://getstream.io/blog/how-a-go-program-compiles-down-
to-...](https://getstream.io/blog/how-a-go-program-compiles-down-to-machine-
code/)).

edit: Actually most of my knowledge comes from
[https://news.ycombinator.com/item?id=9622417](https://news.ycombinator.com/item?id=9622417),
I had bookmarked it.

~~~
msbarnett
> And in Java it didn't stop there; the people (person?) behind generics moved
> on to create Scala, a language with even more features and a notoriously
> complicated 25-step compilation process

What does this have to do with anything? Scala is complex to compile for
reasons far, far beyond generics. Are generics somehow _guilty by association_
because an engineer who was once involved in an implementation of them went on
to build some other complex thing?

------
013a
I don't like this at all. This is a classic example of a feature where, in the
right hands its very powerful, but in the wrong hands it will ruin the
simplicity of the logic it touches. Go's magic was really in its ability to be
placed in the wrong hands and still end up with a serviceable program.

~~~
ailideex
I find the position that the real solution to incompetence is to just give
people blunt knives to be utterly unsatisfactory. If someone cannot understand
how exceptions work for example I have a hard time seeing how removing this
massive burden from their minds will result in good outcomes. Being easy to
use for people that probably would be better off doing something else is not a
solid design principle.

Java went down this route, ah, we will remove operator overloading, that will
make it simpler, because nobody can ever write a function named equals which
does not do what you expect it to... and the end result is horrible and does
not prevent people from doing amazingly dumb things.

~~~
logicchains
To quote Rob Pike:

>The key point here is our programmers are Googlers, they’re not researchers.
They’re typically, fairly young, fresh out of school, probably learned Java,
maybe learned C or C++, probably learned Python. They’re not capable of
understanding a brilliant language but we want to use them to build good
software. So, the language that we give them has to be easy for them to
understand and easy to adopt.

If Google, with its intensive algorithmic interviews, can't manage to hire
people capable of understanding something like:

    
    
      func max(T: Comparable)(a, b T) { 
        if a < b { 
          return b 
        } else {
          return a }
        } 
      }
    

And why we'd prefer that over having to rewrite the function for int8, int16,
int32, int64, uint8, uint16..., then what hope is there for anybody else?

~~~
013a
The funny thing is; your code has a syntax error (two, actually).

Its dumb to nitpick, but its symbolic of the problem; even if you're so sure
that you have no mistakes, that what you've written is well designed and will
work well... you're probably wrong. Even the simplest blocks of code will have
issues, and the language should do everything it can to help this.

No one likes to be told that all they deserve is a blunt knife, but frankly,
it's more true than it isn't. I'll gladly wield that blunt knife if it means I
get to build awesome products that don't break at 2am.

(Edit) I made a mistake in my analysis of your code; there's actually three
errors. Everyone makes mistakes. Our tools shouldn't encourage us to make
more.

~~~
logicchains
Now imagine if I'd copy-pasted that code for every builtin int and float type;
there'd be ~ 36 errors.

~~~
013a
That's a fair point, but it harkens back to the argument we see a lot for
generics; "we'd have to repeat that 5 times for int8, int16, int32, int64, and
just int, not to mention floats!".

I think there's a problem of complexity with that core argument. Why are you
replicating this function 5 times? Why not just use `int`? Or just `float`? In
other words, you're creating a solution that is (probably) too complex, and to
manage that complexity you're asking for more language complexity. How about
we attack the root of the issue: why is the solution so complex? Can we reduce
that complexity?

More often than not, we can. Developers tend to over-engineer things, and Go's
type system is a forcing function toward simplicity. Every once in a while, we
run into a problem that would benefit from generics. But its _very_ rare. The
cost of "having" generics in every situation, including many where you don't
need them, far outweighs the cost of being forced to grab `interface{}` or
`reflect` in the very few situations where you don't have them. The problem
is: It's not as obvious that this cost is higher. That's the challenge the Go
team has had over the past few years; convincing us, who hold a loaded gun
pointed straight at our foot, to not pull the trigger and turn this language
into Java2.

------
lalaithion
I still think that, for the most part, Go doesn't need contracts (yet). I
think it's common, coming from the object oriented world, to expect
polymorphism to be intrinsically tied to method overloading. But it doesn't
have to be.

Of the 12 examples at the end of the draft, there is only one real contract
used, `comparable`, which could be easily done without contracts (but with
generics) by passing in an extra argument `lessThanOrEqual func(T, T) bool`.

The other kind of contract is harder to do without contracts, as it is the
contract which combines all numeric types. However, most of the examples would
be possible to write, albeit a little more messily, with a parameter `toDouble
f(T) double`. Alternatively, we could go the route of baking in a few special
contracts into the language, such as a numeric contract, just like how map and
slice are baked in polymorphic structures.

What do we gain from writing code this way? It leaves the Go language with
more freedom from the future. Everyone agrees Go needs polymorphism. So add
polymorphism. But as we can't agree on contracts, we can just leave it out.
We'll get 90% of the benefits upfront, and then we can wait and see what are
the actual edge cases we run into with contracts, and decide where to go from
there.

~~~
stcredzero
_I think it 's common, coming from the object oriented world, to expect
polymorphism to be intrinsically tied to method overloading. But it doesn't
have to be._

Please provide some examples. For example, I don't think polymorphism in
direct variable references is all that good.

 _It leaves the Go language with more freedom from the future. Everyone agrees
Go needs polymorphism. So add polymorphism._

This statement confuses me. Go has polymorphism.

~~~
lalaithion
If you go and look at the Map/Reduce/Filter, Channels, Containers, and Append
sections of the examples, there isn't any use of contracts. All that is
required for those sections to work is to declare a type parameter, and then
allow that parameter to be filled by any type, but still typecheck that all of
the variables with the same parameter are the same type at the calling site.
There's no need to have a contract for which methods can be called on the
type, because we don't need to call any methods or access any fields. In my
experience, this fills most use cases. It allows generic data structures, and
generic functions over those structures.

If you need a little bit of behavior for those types, such as for a TreeMap
data structure or a Sort function, Go's higher order functions make this
possible without contracts, by passing in functions. The draft's Containers
section uses this approach to construct a TreeMap. The SliceFn function,
described in the Sort section, also uses this approach:

    
    
        func SliceFn(type Elem)(s []Elem, f func(Elem, Elem) bool) {
         Sort(sliceFn(Elem){s, f})
        }
    

Although this does use method overloading via contracts in implementation (via
Sort), it doesn't need it. I expect that Go's existing interfaces (which yes,
are a form of polymorphism, mea culpa) + unbounded parametric polymorphism are
enough to solve 90% of Go's current issues.

The more complex cases are still possible to handle with parametrically
polymorphic structs containing functions. Yes, that's bad code, but we'd be
able to look for this use case, judge how often it occurs, and use it's use
cases as data to inform further discussions on contracts.

The only case I struggle to easily deal with is the numeric case. My solution
doesn't allow generic numeric functions, although I think there's room to
explore that in more detail.

~~~
stcredzero
_If you go and look at the Map /Reduce/Filter, Channels, Containers, and
Append sections of the examples, there isn't any use of contracts._

I see. I'm of the opinion that the Go team might be more cautious and
introduce a more limited form of generics first, which seems in line with what
you're saying.

 _In my experience, this fills most use cases. It allows generic data
structures, and generic functions over those structures._

How would one make something like ConcurrentMap? Perhaps some operations on a
map could be exposed and "overridden?"

~~~
lalaithion
It would be simple to write a ConcurrentMap.

What is more difficult is writing a function which accepts both ConcurrentMaps
and TreeMaps. In Java, they would both inherit from Map. In this
proposal(/Rust/Haskell) they would implement the same
contract(/trait/interface). In my proposal... there isn't an elegant solution.

In some cases, you can write the function to be generic over an insertion
function `insert func(T)`, if all you need is assertion, and then you can pass
`treeMap.insert` as the parameter. If you really truly need to be generic over
the entire `Map` interface, that is one of the cases that I claim only takes
up 10% of the cases. In that case, you could define a type which contains all
of the functions a Map needs, and then you could construct that and manually
pass that in.

This can be viewed as a desugaring of contracts. One implementation that
contracts could have would be to, at runtime, construct and pass a value
containing the functions that the contract specified are possible.

~~~
higherkinded
It's "typeclass", not "interface". Though it works similar and is, in fact,
just a way to define a constraint.

As of your described way of desugaring the contract, looks way too cluttered.
It's exactly the reason why Reader and ReaderT exist in Haskell. Even though
the proposal's contract syntax doesn't look very beautiful, it's still
drastically better than keeping track of drilled structs of functions, the
latter will become a mess really really fast, so having a way to conveniently
constraint the accepted types while having a full-blown parametric
polymorphism is essential for a modern language. Not sure exactly why anyone
would be against that.

~~~
lalaithion
Sigh, I really need to read my posts before submitting. You're right, of
course it's typeclass.

The whole point is to implement the bare bones of a parametric polymorphic
system without constraining further development of the language. Additionally,
it discourages complexity and requires explicitness, two goals of Go.

As far as my description of desugaring the contract, that is literally how
Haskell implements typeclasses.

~~~
higherkinded
Well, yeah, Reader is that kind of thing.

I guess that the complexity people are concerned about is that once you
implement a parametric polymorphism, HKTs are inevitable and that probably
requires some brain flexing in order to get the meaning of a structure, which
defeats the main purpose that is being explicit and simple. Pretty much a
deadlock. I now understand why the community is so in odds with the parametric
polymorphism as the concept but I'm also sure that simplicity of use and
convenience of writing a robust solution is the two things that require it.
You can't keep your head near the sand and avoid HKTs when you want
flexibility and you can't allow that type of polymorphism whilst striving for
what's Go trying to do.

While understanding the both standpoints, though, I still hope that Go will
get parametric polymorphism as it's just so much more pleasant and convenient
than carrying around a mess of structures to be explicit no matter the costs.

------
koblas
The proposal has a very short statement about implementation. If you think
about this very carfully, you realize that part of the compilation speed of go
allows you to compile a single package into code without having to leak out
all of you abstractions.

If you have contracts/generics then you need to have un-compiled code as part
of your exports. Which is a huge break from the current approach.

~~~
munificent
Yeah, I'm _very_ interested in how they plan to integrate this into separate
compilation. My understanding from C++ and C# is that this is a hard problem.

~~~
steveklabnik
In Rust, we end up storing the necessary metadata inside of the pre-compiled
library, so that when you use it from another library, the compiler knows how
to do the right thing.

------
iainmerrick
Why “Contracts” and not “Generics”? To make it less contentious, maybe, or to
manage expectations?

~~~
apta
golang likes to reinvent the wheel and call it something else. We've already
seen it with exceptions (which they called "panics"). Contracts already mean
something else (see contracts in C++, C#, D, etc.).

What they're proposing here are basically trait bounded generics. They just
don't want to use the word "generics" because of the following attitude:

"The key point here is our programmers are Googlers, they’re not researchers.
They’re typically, fairly young, fresh out of school, probably learned Java,
maybe learned C or C++, probably learned Python. They’re not capable of
understanding a brilliant language but we want to use them to build good
software. So, the language that we give them has to be easy for them to
understand and easy to adopt."

~~~
NateDad
Man you Go haters LOOOVE to trot out that quote from 2014.

But you know what, go _is_ easy to learn, and that's been awesome in my
experience. I can (and have) hired people who have never written go, and
they're productive in a week or two.

~~~
apta
That's not really a good metric to target. They can be productive in a week,
and because the language is very weak and verbose, you'll end up with a messy
code base. I see this all the time at an employer.

~~~
NateDad
....in Go?

------
dnr
This is really bike-shed-y, but:

I'm disappointed they didn't go with «» or similar to set apart type
parameters. The answer in the doc is that they "couldn't bring ourselves to
require non-ascii", but there's an easy way to handle this without _requiring_
non-ascii characters: let "(type …)" and "«…»" be syntactically identical, and
have gofmt change the former to the latter. Basically everyone uses gofmt all
the time, so all the code everyone reads would use the more visually
distinctive syntax, but it would be easy to type using ascii only.

~~~
spraak
Why not just << and >> then?

~~~
dnr
That doesn't work for the same reason that < and > don't work, right? They are
existing operators in the language, so the parser would need unbounded
lookahead. Using a different set of characters is unambiguous (and
additionally is more compact).

------
tmaly
I think the examples at the end of the document really show the power of the
proposal.

But, there are some aspects that will make code appear more complex or
challenging to read. It will certainly make it more challenging for beginners.

Appearance of the code with type switches and issue with the idea of Iterator
and doing something two different ways were the only parts that stood out to
me besides increased complexity

------
samprotas
"Although functions may have multiple type parameters, they may only have a
single contract."

Anyone else find this limitation a bit disappointing? Seems like a somewhat
arbitrary restriction that limits the usefulness of this feature. I hope it
doesn't take another 10 years for this to be changed...

~~~
monkeyfacebag
it looks like contracts are composable the same way interfaces are so I don't
know how much of a limitation this will be in practice.

~~~
samprotas
So I am a bit unclear on this from the proposal.

Composing contracts is a slightly more verbose fix for allowing multiple
contracts for a given type (just make a composed contract and specify that).

Using composed contracts, allowed:

    
    
        func Foo(type T PrintStringer)(s T) {...}
    
    

I read this as, while a function can have multiple type parameters, only one
contract can be specified _in total_.

Not allowed (function uses "setter" and "stringer"):

    
    
        func Bar(type B setter, S stringer)(box B, item S) {...}
    
    

Maybe I'm misunderstanding though.

In other languages with parametric polymorphism, the real re-use comes in by
allowing functions like Bar to be used for any combination of "constraint
implementing" types.

~~~
msbarnett
> Maybe I'm misunderstanding though.

As I read it, while you can't do:

    
    
        func Bar(type B setter, S stringer)(box B, item S) {...}
    

directly, you accomplish the same thing via

    
    
        contract SetterStringer(B, S) {
            setter(B)
            stringer(S)
        }
        func Bar(type B, S SetterStringer)(box B, item S) {...}
    

so in practice it's basically the same thing, you just have to explicitly
specify the contract the function conforms to via a composition of the two
other contracts.

~~~
samprotas
This is a good point.

So maybe my concern is more a verboseness issue rather than expressiveness.

That said, it would be nice if there was some commentary on whether an
implementation like Swift’s was considered and ruled out for some reason. As
it reads now, I stand by my original comment that this restriction seems a bit
arbitrary.

~~~
msbarnett
I’d be very interested in that as well, and I agree it seems verbose simply
for the sake of maintaining a one-one func-contract correspondence.

Maybe there’s some trade-off I’m not seeing here, though.

------
iio7
I cannot fully express the frustration I deeply feel with people constantly
proposing changes to Go that will turn it into something that is no longer Go!

I know where this specific proposal is coming from, but I feel that Robert and
Ian are being pushed by the constant noise made by people coming from other
languages, people who like those who made the horrible "try" proposal, seem to
be trying really hard to ruin Go by turning it into yet another complex
monster.

Not a day goes by without someone making a new proposal that is trying to
change the very thing that made Go so unique and lovable!

All the proposals that has been made so far exists in several of the other
popular programming languages. Use one of those if you really want the added
complexity - leave Go just the way it is!

------
utahcon
I am totally against generics, if you want generics, go to Python, or
somewhere else that allows them. The beauty in Go is that it is static, and
generics are not strict. I agree with wybiral [1]. This only serves to add
complexity that gains very little, and opens Go to being less strict and there
for less reliable and more problematic at compile time .

[1]
([https://news.ycombinator.com/item?id=20555477](https://news.ycombinator.com/item?id=20555477))

edit: changed "strict" to "static"

------
hota_mazi
Took me a while to realize that this is not about "contracts" the way this
concept is generally understood, but Go's versions of generics.

------
baby
I really see no reason for these. The difference could be easily seen in the
types themselves. I’ve posted that elsewhere but you could have an

    
    
      func thing(stuff _.T, stuff2 _.k)
    
    
      Or
    
      func thing(stuff g.K)
    
    

And so on.

Also want to point out that there is a LOT of Golang code that is being
shipped and used in the world and none of it uses generics. Do we really need
them?

~~~
continuational
A lot of assembly code was being shipped before we had better languages.
That's no reason to be stuck with a type system of the previous millennium.

~~~
baby
I honestly think generics are like oop. Vastly overestimated.

------
yorozu
> Type assertions and switches

I find it especially unorthogonal that you can type switch on generic
parameters like they are interfaces. If I have a method that takes interface
parameters, should I just always use generic (unboxed) parameters instead?

------
iamgopal
Why not extend the interface ? It would be less of an interface and more of a
generic but may provide easier upgrade path, wouldn't it ?

~~~
mseepgood
There's a whole section in the document literally titled "Why not use
interfaces instead of contracts?"

------
paedubucher
How (and when) does a Draft Design become a Proposal to be discussed
officially on GitHub?

------
zemo
tbh I just want sum types but whatever

