I'm not sure if i understood everything correctly. type structField(type T) struct { a int x T }
But this is a generic type, not a constraint for a type, isn't it? Constraint is this: type Custom interface{ type int, float64, uint64 } type structField(type T Custom) interface { type struct { a int; x T }, struct { b int; x T }, struct { c int; x T } } What am I missing? And I think it rather well highlights what I am talking about. Consider the following pseudocode: type ConstraintIWant interface { ConstraintA || ConstraintB // i don't care which one, either is fine } Maybe i'm looking in the wrong place, but i think that to do this I have to extract `CommonPart` (`Custom`) from `ConstraintA` and `ConstraintB`, add `CommonPart` back to both constraints so they aren't broken and then request `CommonPart` in `ConstraintIWant`. This is Java-style inheritance. It isn't bad, but i think that C++-style composition is somehow more in the spirit of Go. Strictly personal opinion of course. Older proposal described constraints as expressions, so to me it appears superior because I could potentially write an expression I want without asking for more features to be added into the compiler. Although i'm no longer sure if I understand what Go generics are supposed to do. You have the point that it's probably for a specific purpose, generic types something something, i don't know, but it probably isn't supposed to do what C++ concepts can do. Maybe i'm just spoiled. If this gets us to where we want to be - that's great, I'm happy if you're happy. It just doesn't feel quite right and i feel like this proposal could benefit from discussion on composition and stuff like that. чт, 23 июл. 2020 г. в 06:29, <lx.gu...@gmail.com>: > > Hey! I would like to join the discussion and add my 5 cents here, since I > have been criticizing the contracts draft and I would like to show what were > my points in order to support the current design draft. > > > I believe the original problem for the generics is to allow the same function > to work on values of different types. So that You do not need to write > something like AddInt, AddInt32, AddInt64 and so on. This is an example of > situation when we would like to write something like Add(T) and make that T > to be one of the list: int, int32, int64 - all of the types what we have the > Add function defined for. > > > In this case Add(T) acts much like an expression evaluating to a different > function depending on the T. > > > This can be called “parametrization by type” or “type parametrization” and I > believe it is an exceptionally simple and straightforward way to do it. > > > Let me say a bit more about problems where need of contracts arise. > > > When we are writing in a context of type-parametrized function something like: > > > var a []T = []int{1, 2, 4} > a[2] = 3 > > > It is okay to write []T[2] = T (assign a value of type T to an element of > array of type T by index 2) and we do not need to care what is T. > > > But when we change a bit: > > > var a T = []int{1, 2, 4} > a[2] = 3 > > > It becomes hard to judge whether we are allowed to write T[2] = 3 (assign 3 > to an element of type T by index 2). In order it to work, we need to state > somehow [2] is allowed for T. There are a few ways to achieve it and the > first one is the contracts: > > > type T interface{ > require(a, b) { > a[2] = b > } > } > > > Okay, now, let’s say, we are allowed to write T[2] = 3. But let’s try a > harder example > > > var a T = []int{1, 2, 4} > a[2] = 3 > a = append(a, 4) > > > This will not work with the previously defined contract T, since a > map[int]int value also fits it. > > > Should we write something like this: > > > type T interface{ > require(a, b) { > a[2] = b > a = append(a, b) > } > } > > > But what is the point in such syntax? How the compiler could guess and how > exactly it should work? What if we write random garbage in the require body, > will it allow us to write it right in the code without any checks? > > > type T interface{ > require(a, b) { > a *@#&@#$= b > } > } > > var a T *@#&@#$= b > > > Of course, we can restrict it to allow using only operators like [], == and > so on, still how can we distinguish between a map and an array? > > > type T interface{ > require(a, b, i) { > a[:] // only arrays and slices have this operator > i++ // i is an integer > a[i] = b // b is an element of array a > } > } > > > Is this enough to define what we want? Such a contract would also require the > append builtin to be changed, but I believe it will only clutter up the > implementation with unnecessary type assertions. > > > These “requires” act very much like predicates and are just tricky way to > “shortly” define a list of types. That is to say, a++ means any type having a > ++ operator defined for it no matter how exactly (which is a problem itself). > Anyway, it is very easy, to define such a contract with a list, which is the > second way of saying T[2] = 3 statement is allowed: > > > type Incrementable interface{ > type int, int32, ... > } > > > Let’s get to Your example: > > > type structField interface { > type struct { a int; x int }, > struct { b int; x float64 }, > struct { c int; x uint64 } > } > > > It is clearly visible, that we need to parametrize by a single field type: > > > type Custom interface{ > type int, float64, uint64 > } > > type structField(type T Custom) interface { > type struct { a int; x T }, > struct { b int; x T }, > struct { c int; x T } > } > > > Depending on the context, we also can write it like this: > > > type structField(type T Incrementable) interface { > type struct { a int; x T }, > struct { b int; x T }, > struct { c int; x T } > } > > > And even further, we can consider such a change: > > > type structField(type T) struct { > a int > x T > } > > > And such a refactoring becomes now a pattern. > > > Instead of writing > > > type T interface{ > type map[int]string, map[int]bool > } > > > We almost always should write: > > > type Z(type T) map[int]T > > > It is a way stricter way of defining not only “contracts” but also types > allowing us to perform such things like appending elements to arrays, taking > element of a map by its key, accessing a struct field and much more while > remaining generic enough. > > > Thanks for the attention, I hope it helps. > > -- > 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/164a69fd-50f3-4a0e-ad9d-5e243c3b007bo%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/CAMteYTYAtBmP7JbGq2dAZepApccp2gNM_MjG%3D5Rf__tecT7beQ%40mail.gmail.com.