> This is clearly a key issue.  I happen to think that contracts are
> fairly clear for the reader: take your type argument and see if the
> contract body works.  I feel that everyone who knows Go will be able
> to do that.  But maybe that's just me.

For complicated contracts—and I mean even ones reduced by a tool to
their minimum—that turns into a puzzle solving contest. For ones that
aren't reduced, it's easy for them to be misleading, like: x % 5 == 0

I agree that it's something where eventually standardized practices
and tooling would fill in the gaps to the extent that these issues
would come up rarely. I'm mostly worried about the time between their
introduction and the stabilization because it will be measured in
years and remembered for longer.

> Something like the convert example in the design draft would still
> need contracts that are unlikely to be defined in the standard
> library.

In my draft proposal, I handled this like
  (type S, T interface{} | S(T))
which introduces two type parameters with no restrictions other than
that values of T must be convertible to S. It's treated specially but
it is somewhat special because it's a property between two types. It
was the only such property I found necessary.

> It's not necessary to support fields but I think it really is
> necessary to support operators.  I claim that, other than type safe
> data structures, the number one generic function that people want to
> write is Min (or Max).  I don't think we can plausible add a generics
> system to Go that doesn't permit writing a Min function.

Hypothetically, if Go introduced generics where you couldn't write
min/max but included min/max builtins, what's the next smallest
example where operators are necessary?

I'd argue that min and max are the only ones that would seriously be
impacted. Every other example is large enough that any boilerplate to
wrap methods would pale compared to the coding saved by being able to
safely reuse a large chunk of code.

I'd be happy with generics if I needed to sometimes write a little bit
of code to do a few  things but didn't have to write a tremendous
amount of code to do most things. That seems like a good trade off,
even if it's not necessarily a popular one.

But you can't really write a generic min/max with the contracts proposal.

You can write a min/max that operates on all the ordered types, sure.

(The one in the proposal has a bug because it accepts float types but
doesn't handle the special cases for floats. That can't be repaired
because while you could assert to float32 or float64 you couldn't
assert to type MyFloat float64. Though I'm sure there's some contract
where you can specify < but not floats.)

You could also write a min/max that operates on types with a Less
method, like you could under the various just-interfaces schemes.

But you can't write one that works on both.

If there were operator overloading any type could have < and you could
specify < in interfaces and then you could work on primitive and user
types equally.

But even then you still couldn't write a min/max that transparently
works on time.Time since it has Before not Less.

To unify those cases you need something like haskell-style typeclasses
where you can say this type satisfies this contract using these
methods/operators.


Even ignoring that, the contracts proposal doesn't let you write a
generic variadic min/max since there's no way to get the
smallest/largest value of a type so you can't write

func min(type T ordered)(ts ...T) T {
  min := LargestPossible(T) // not possible
  for _, t := range {
    if t < min {
      min = t
    }
  }
  return min
}

You could get around that with the signature (firstOne T, rest ...T)
but there's a larger point about the necessity of knowing numeric
limits and properties for writing that kind of generic code in there.


One way around the asymmetry between operators and methods that
doesn't introduce any issues like operator methods or any of that is
to have a package that defines types like

package basic // not the best name

type Int int

type Slice(type T) []T

// and so on for all the primitive and basic composite types

and give all of those methods like Less and At. You'd have to do
int(min(basic.Int(x), basic.Int(y))) but at least you wouldn't have to
write the trivial wrapper methods. Not especially pretty.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to