On Mon, Aug 10, 2020 at 10:46 AM 'Richard Oudkerk' via golang-nuts
<golang-nuts@googlegroups.com> wrote:
>
> Another way to bridge the gap between builtin and custom types could be to 
> have a package op that has functions that delegate to either an operator or a 
> method.  Then you could write generic functions like
>
> func Min[type T op.Lessable](a, b T) T {
>   if op.Less(a, b) {
>     return b
>   }
>   return a
> }


Would it be possible to make it explicit instead of trying to combine
builtin types and others?

type Number interface {
   type int, int8, int16, int32, int64, unit, int8, int16, int32,
uint64, float32, float64
}

func Min(type T Number)(a, b, T) {
  if a<b {
     return a
 }
 return b
}

type Lessable interface {
   func LessThan(interface{}) bool
}

func Min(type T Lessable(T))(a, b T) {
  if a.LessThan(b) {
     return a
  }
 return b
}

This would be similar to c++ template specialization. Specializations
could be limited to built-in types to limit ambiguity.

}

>
> For each function Foo in op, op.Foo(a, ...) would delegate to either 
> a.Foo(...) or to a builtin operator, and there would be an associated 
> interface op.Fooable. For example
>
> op.Add(a, b) is equivalent to a.Add(b) or a + b
> op.Len(a) is equivalent to a.Len() or len(a)
> op.Get(a, i) is equivalent to a.Get(i) or a[i]
> op.Range(a, f) is equivalent to a.Range(f) or for k, v := range a { f(k, v) }
>
> I don't think op.Lessable is expressible with the latest proposal though.
> On Monday, 10 August 2020 at 02:29:53 UTC+1 Ian Lance Taylor wrote:
>>
>> On Fri, Aug 7, 2020 at 4:33 PM Patrick Smith <pat42...@gmail.com> wrote:
>> >
>> > I like the second draft for generics. It seems to me a large
>> > simplification and improvement over the first draft. Considering just
>> > the state of Go today, I would be quite happy with this, even if it's
>> > not perfect. Thanks to Ian, Robert, and everyone else for their work
>> > on this.
>> >
>> > Also, I would vote for square brackets over parentheses.
>> >
>> > But I do have concerns related to the future development of Go. In
>> > particular, if we think it likely that a future version of Go will
>> > allow operator overloading, then perhaps type lists are not the best
>> > choice.
>> >
>> > To my mind, the biggest defect in the design draft is that we can't
>> > write generic functions and types that work transparently with both
>> > builtin and user-defined types (that do not inherit appropriate
>> > behavior from an underlying builtin type). For example, we can't write
>> > a
>> >
>> > func Min[type T ...](a, b T) T { ... }
>> >
>> > that works both when T is int and when T is
>> >
>> > type BigInt struct { i *big.Int }
>> >
>> > Instead, we would use workarounds such as writing two versions of Min,
>> > or passing in an adaptor function or object; in the case of Min, a
>> > comparison function. And that's OK, especially in an initial version
>> > of generics.
>> >
>> > But generics would be significantly easier to use if we could write
>> > functions that work on both builtin and user-defined types. The two
>> > most likely candidates for allowing this seem to be operator
>> > overloading (where BigInt might have a method named "<", "operator<",
>> > or some such, that allows it to be used with the < operator) and
>> > methods on builtin types (where int might be given a method named Less
>> > with the same behavior as the < operator). Of course, other solutions
>> > could be imagined, but I'll confine my speculations to those two.
>> >
>> > Now let's try to imagine how sorting slices might be implemented in
>> > the standard library in various futures. Of course, the current sort
>> > package would have to be kept and maintained for a long time.
>> >
>> > If Go2 implements the current draft with type lists, then we might add
>> > a sort2 package containing something to:
>> >
>> > func SliceBy[type T](s []T, less(T, T) bool) { ... }
>> >
>> > type Ordered interface { // Copied from the draft
>> > type int, int8, int16, int32, int64,
>> > uint, uint8, uint16, uint32, uint64, uintptr,
>> > float32, float64,
>> > string
>> > }
>> >
>> > func Slice(type T Ordered)(s []T) {
>> > SliceBy(s, func(a, b T) bool { return a < b })
>> > }
>> >
>> > type Lesser[type T] interface { Less(T) bool }
>> >
>> > func SliceByLess(type T Lesser[T])(s []T) {
>> > SliceBy(s, T.Less)
>> > }
>> >
>> > All well and good. Now say time goes by and Go3 adds operator methods.
>> > Nothing in the sort2 package expresses a unified sort using operator
>> > methods, so we need a new package sort3:
>> >
>> > func SliceBy[type T](s []T, less(T, T) bool) { ... }
>> >
>> > type Lesser[type T] interface { <(T) bool } // Or whatever the syntax is.
>> >
>> > func Slice(type T Lesser)(s []T) {
>> > SliceBy(s, func(a, b T) bool { return a < b })
>> > }
>> >
>> > (We might just add sort3.Lesser and sort3.Slice into the sort2 package
>> > under different names, but I suspect the aim would be to eventually
>> > deprecate sort2.)
>> >
>> > The effects will ripple through other code, both in and outside the
>> > standard library. Suppose some Go2 code has a chain of generic
>> > functions A calls B calls C calls D, where each exists in two
>> > versions, one for builtin types and one for user-defined types, and
>> > the two versions of D call sort2.Slice or sort2.SliceByLess. When Go3
>> > with operator methods arrives, if we want to unify these, we have to
>> > write a third version of each of A, B, C, and D, where D calls
>> > sort3.Slice.
>> >
>> > On the other hand, suppose Go2 has type lists and Go3 gives builtin
>> > types methods corresponding to operators. Assuming the name Less is
>> > used for <, sort2.SliceByLess now handles both builtin and
>> > user-defined types, so we don't need a sort3 package. And in the ABCD
>> > scenario, we can just keep the SliceByLess version of each, and
>> > quietly let the sort2.Slice versions vanish as they become unused.
>> >
>> > [Important point===>] This means that if Go2 has type lists in
>> > interfaces, there will be a strong incentive for Go3 to give builtin
>> > types methods, even if we think that operator overloading is otherwise
>> > a superior solution, because operator overloading will require much
>> > more new code to be written.
>> >
>> > Instead of using type lists, suppose Go2 allowed interfaces to require
>> > the presence of specific operators. Then the sort2 header might look
>> > like this:
>> >
>> > func SliceBy[type T](s []T, less(T, T) bool) { ... }
>> >
>> > type Lesser[type T] interface { <(T) bool }
>> >
>> > func Slice(type T Lesser)(s []T) {
>> > SliceBy(s, func(a, b T) bool { return a < b })
>> > }
>> >
>> > type MLesser[type T] interface { Less(T) bool }
>> >
>> > func SliceM(type T MLesser[T])(s []T) {
>> > SliceBy(s, T.Less)
>> > }
>> >
>> > (Note that the first part is identical to the previous sort3 header.
>> > But in Go2 we also need MLesser and SliceM in order to handle
>> > user-defined types.)
>> >
>> > This leaves us more easy options in Go3. If Go3 implements operator
>> > overloading, then sort2.Slice now handles both builtin and
>> > user-defined types, and code using sort2.SliceM can be allowed to
>> > wither away. If Go3 implements an int.Less method, then sort2.SliceM
>> > is now the good version, and code using sort2.Slice can be allowed to
>> > wither away as it becomes unused.
>> >
>> > So maybe the alternative of allowing interfaces to require specific
>> > operators deserves another look, to see if it's really not viable in
>> > Go2. I would suggest that perhaps the initial version of generics need
>> > not support all operators; maybe it's enough to support only those
>> > that apply to numeric types (string should be accepted by interfaces
>> > requiring +, <, and other comparison operators). Or maybe a slightly
>> > larger subset of operators would do.
>> >
>> > As a final note - this post, like all speculations about the future,
>> > is rather fuzzy. I realize that. Nevertheless, I think it is important
>> > to realize that the choices we make now carry consequences for our
>> > options after a few years' time.
>>
>>
>> Thanks for the detailed comment.
>>
>> I think the key statement in your argument is this one:
>>
>> > But generics would be significantly easier to use if we could write
>> > functions that work on both builtin and user-defined types. The two
>> > most likely candidates for allowing this seem to be operator
>> > overloading (where BigInt might have a method named "<", "operator<",
>> > or some such, that allows it to be used with the < operator) and
>> > methods on builtin types (where int might be given a method named Less
>> > with the same behavior as the < operator). Of course, other solutions
>> > could be imagined, but I'll confine my speculations to those two.
>>
>> I think this is open to question. In C++, for example, std::sort
>> takes an optional comparison class. In effect, the default if no
>> comparison class is provided is to use operator<. That is a
>> reasonable and appropriate choice for C++. But Go does not have
>> function overloading and does not have default values for arguments
>> (https://golang.org/doc/faq#overloading). So the natural way to write
>> a sort function is to provide a comparison function. That is how
>> sort.Slice works, for example. sort.Sort works differently because it
>> needs three different functions, so they are passed in via a type
>> rather than as an argument. In typical use, people will convert to
>> that type when they call sort.Sort, so they are providing the required
>> functions via a type conversion.
>>
>> If we accept this argument, then in Go it wouldn't be appropriate to
>> write a single function that works on both builtin and user-defined
>> types. Writing such a function would be relying on some sort of
>> default comparison function, which is not the typical Go approach.
>>
>> So instead we need to ensure that it is very easy to pass a comparison
>> function, regardless of whether you are using a builtin type or a
>> user-defined type. And I think that that is already true.
>>
>> Ian
>
> --
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts+unsubscr...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/golang-nuts/f60e0dc5-60ea-4c82-9309-d55fb1b9b3adn%40googlegroups.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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAMV2RqpNVxP%2BjmGLivrdLeQamYgz33F0Os9_pJnzi932obZ_nA%40mail.gmail.com.

Reply via email to