On Tue, Aug 11, 2020 at 8:31 PM Ian Lance Taylor <i...@golang.org> wrote: > To me the point of your String example is: Go already has various ways > of writing generic code. You can use interfaces, methods, and type > assertions. You can use reflection. The generics design draft adds > another way: parametric polymorphism.
The other point is that writing functions or types that work with both builtin and user-defined types is not just good for reducing duplication in the implementation. It also simplifies the use of those functions. I imagine most people don't really care how much effort was required to write fmt.Print. But they would object if asked to write fmt.PrintB(1, " ") fmt.PrintS(big.NewInt(2)) fmt.PrintB(" ", 3) instead of fmt.Print(1, big.NewInt(2), 3) > > A better example might be a function that finds the roots of a > > polynomial with real-valued coefficients ... > Thanks, it's an interesting example. big.Float isn't really the same > as float32 or float64. For example, big.Float doesn't provide any way > to write "a = b + c + d". You can only write, in effect, "a = b + c; > a += d". And big.Float doesn't have == != < <= >= >. Instead it has > expressions like "a.Cmp(b) == 0". So if you want to write a generic > function that works on float32/float64/big.Float, you're going to need > adaptors no matter what. You can't avoid them. > > So in practice I don't think we're going to write the root-finding > function using parametric polymorphism at all. I think we're going to > write it using an interface type. I think we'll need an interface type and parametric polymorphism. type Float interface{ Add(Float) Float } doesn't have the right semantics. > And I think we're going to base > that interface type on big.Float. Then we need to write an > implementation of that interface for float32/float64. And that > implementation is likely to use parametric polymorphism so that we > only have to write it once to handle both float32 and float64. And > for that implementation, type lists work fine. I tried out a few different implementations, evaluating the polynomial instead of finding roots, at https://go2goplay.golang.org/p/g8bPHdg5iMd . As far as I can tell, there is no sensible way to do it with an interface that is implemented directly by *big.Float; we need to wrap *big.Float in a wrapper type in any case. The trouble is that you can write type Float[type F] interface { Add(F, F) F } func Sum[type F Float[F]](a, b F) F { } > > > What you're looking for here, I think, is a case where there is a type > A that implements methods that are isomorphic to the basic arithmetic > and/or comparison operators. And then you want an algorithm that can > be used with that type, as well as with the builtin types. For such a > case, it might be nice to be able to write that algorithm only once. > > And, of course, with the current design draft, we can write the > algorithm only once, provided we write it using methods. We will use > the methods defined by the type A. And we will need an adapter for > builtin types to implement those methods. And that adaptor can be > written using type lists. > > Now, I think your original argument is that in the future we might > decide to introduce operator methods. And we might add those operator > methods to A. And then we can write our algorithm using operators. > But we might have a chain of functions already built using (regular) > methods, and we won't be able to use those with operator methods, so > we will be discouraged from using operator methods. > > So first let me observe that that seems to me like a pretty long chain > of hypotheticals. Is there any type out there today that has methods > that correspond exactly to operators? There is a reason that > big.Float and friends don't have such methods: for any type that uses > pointers, they are inefficient. Is this possibility going to be a > real problem, or will it always be a hypothetical one? > > Second, let me observe that in this example it seems to me that we > will already have the adapter types that add methods to the builtin > types. So the question would be: if we add operator methods, can we > very easily shift those adaptor types from using type lists to using > operator methods instead? And it seems to me that the answer is yes. > If we have operator methods, we will be able to write interface types > with operator methods. And if we do that it would be very natural to > let the builtin types implement those interfaces. And, of course, we > can use those interface types as constraints. So we just have to > change the constraints that our adapter types use from type lists to > interface types with operator methods. And everything else will work. > So, sure, operator methods might lead to some code tweaks. But they > won't require a massive overhaul. > > Or so it seems to me. What am I missing? > > > > > 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 Go does not have default argument values is, I think, merely a > > distraction. > > This is easily handled with multiple functions; one would like to write > > > > func SortBy[type T](s []T, less func(T, T) bool) { ... } > > > > func Sort[type T ...](s []T) { > > SortBy(s, the_natural_order_on_T) > > } > > > > where the constraint on T in Sort expresses that T must have a natural > > ordering. > > Yes. One way to state my point is to note that you had to give the > functions different names: Sort and SortBy. In C++ those two > functions have the same name, where your Sort function is in effect > SortBy with a default argument. > > > To me the point of your String example is: Go already has various ways > of writing generic code. You can use interfaces, methods, and type > assertions. You can use reflection. The generics design draft adds > another way: parametric polymorphism. > > The particular topic here is whether adopting type lists today is > going to tie our hands in the future in a way that we don't anticipate > and that would be bad. It's possible of course, but it's not yet > clear to me that that must be so. > > Thanks for the note. > > 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/CAADvV_v4E9VzLZKjhv3rXfwzyBfWWGFQhhM6nD3uf4xtthzTeg%40mail.gmail.com.