Re: [go-nuts] [Generics] Feedback on updated draft
On Thu, Jul 23, 2020 at 1:12 AM Aleksey Tulinov wrote: > > First of all, thank you for taking your time to answer this topic. I > know some stuff i say is dumb, i appreciate your patience. > > To answer your question: i would assume that OR is about as useful as > AND or NOT. > > type Constraint interface { >Sequence && !Channel > } > > With that said. So far i'm only a theorist and you are probably the > best expert in this field, so I would rather listen to what you have > to say than express my own ideas, even though they are not really my > own and credit has to go to other people. > > I get it, there is a scope for generics and you are asking this > question because there are other considerations about the scope, > usefulness, timeline and this is a problem with more than one factor > and all factors have to be considered. I hope generics version 1 will > be received very well, and I'm actually sure they will. > > I absolutely trust your judgement on this matter, of course I don't > have practical experience with generics in Go, but I saw something > that I didn't like. I'll let someone who applied generics in the field > to provide practical feedback and I will be glad to hear that they are > amazing. Sorry for wasting your time. This is undoubtedly an > improvement to the language and it is very needed. Good luck. > > >I've tried to outline what we need from generics in > https://blog.golang.org/why-generics. > > Right. I must have read this too, but probably a year ago and forgot > about it. This all makes sense now. Thank you very much. I appreciate the comments. It's not at all a waste of time. Thanks for writing. Ian > чт, 23 июл. 2020 г. в 09:16, Ian Lance Taylor : > > > > On Wed, Jul 22, 2020 at 9:41 PM Aleksey Tulinov > > wrote: > > > > > > 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. > > > > There is no question that the older syntax was more powerful. And I > > suspect that your syntax is also more powerful. But we got extensive > > feedback that the approach was too complex. C++ and Go have very > > different tolerances for complexity. > > > > You raised the possibility of a constraint that permits either > > constraintA or constraintB. I think that supporting that kind of > > constraint would be more powerful than what the current design draft > > provides. But is it a necessary feature? How often does one want to > > use such a constraint? > > > > I've tried to outline what we need from generics in > > https://blog.golang.org/why-generics. I think that that describes the > > minimum set of required features for a viable language change. The > > possibility of alternative constraints is not in that set. Supporting > > everything we can think of is not a goal. It's not an inherently bad > > thing. But anything more powerful than the minimum set has to not > > make the language more complicated to understand. > > > > Within that context, I think it's a great idea to discuss composition > > or inheritance. If we can come up with something that is more > > powerful and less complex, that would be great. > > > > 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/CAOyqgcXjPHxnMA3d_z7snUHcOX0%2B4ZVTQFo79FWyebayKudmJg%40mail.gmail.com.
Re: [go-nuts] [Generics] Feedback on updated draft
Ian, First of all, thank you for taking your time to answer this topic. I know some stuff i say is dumb, i appreciate your patience. To answer your question: i would assume that OR is about as useful as AND or NOT. type Constraint interface { Sequence && !Channel } With that said. So far i'm only a theorist and you are probably the best expert in this field, so I would rather listen to what you have to say than express my own ideas, even though they are not really my own and credit has to go to other people. I get it, there is a scope for generics and you are asking this question because there are other considerations about the scope, usefulness, timeline and this is a problem with more than one factor and all factors have to be considered. I hope generics version 1 will be received very well, and I'm actually sure they will. I absolutely trust your judgement on this matter, of course I don't have practical experience with generics in Go, but I saw something that I didn't like. I'll let someone who applied generics in the field to provide practical feedback and I will be glad to hear that they are amazing. Sorry for wasting your time. This is undoubtedly an improvement to the language and it is very needed. Good luck. >I've tried to outline what we need from generics in https://blog.golang.org/why-generics. Right. I must have read this too, but probably a year ago and forgot about it. This all makes sense now. Thank you very much. чт, 23 июл. 2020 г. в 09:16, Ian Lance Taylor : > > On Wed, Jul 22, 2020 at 9:41 PM Aleksey Tulinov > wrote: > > > > 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. > > There is no question that the older syntax was more powerful. And I > suspect that your syntax is also more powerful. But we got extensive > feedback that the approach was too complex. C++ and Go have very > different tolerances for complexity. > > You raised the possibility of a constraint that permits either > constraintA or constraintB. I think that supporting that kind of > constraint would be more powerful than what the current design draft > provides. But is it a necessary feature? How often does one want to > use such a constraint? > > I've tried to outline what we need from generics in > https://blog.golang.org/why-generics. I think that that describes the > minimum set of required features for a viable language change. The > possibility of alternative constraints is not in that set. Supporting > everything we can think of is not a goal. It's not an inherently bad > thing. But anything more powerful than the minimum set has to not > make the language more complicated to understand. > > Within that context, I think it's a great idea to discuss composition > or inheritance. If we can come up with something that is more > powerful and less complex, that would be great. > > 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/CAMteYTYWvtfkRo13dfE1XJyWkwK1EKfKRuQP-22dkLbObpMUwg%40mail.gmail.com.
Re: [go-nuts] [Generics] Feedback on updated draft
On Wed, Jul 22, 2020 at 9:41 PM Aleksey Tulinov wrote: > > 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. There is no question that the older syntax was more powerful. And I suspect that your syntax is also more powerful. But we got extensive feedback that the approach was too complex. C++ and Go have very different tolerances for complexity. You raised the possibility of a constraint that permits either constraintA or constraintB. I think that supporting that kind of constraint would be more powerful than what the current design draft provides. But is it a necessary feature? How often does one want to use such a constraint? I've tried to outline what we need from generics in https://blog.golang.org/why-generics. I think that that describes the minimum set of required features for a viable language change. The possibility of alternative constraints is not in that set. Supporting everything we can think of is not a goal. It's not an inherently bad thing. But anything more powerful than the minimum set has to not make the language more complicated to understand. Within that context, I think it's a great idea to discuss composition or inheritance. If we can come up with something that is more powerful and less complex, that would be great. 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/CAOyqgcUcob2oNMwnhuOSUR3NDaM_Cvo%3DaicOJcMhG5C%3DjskzjA%40mail.gmail.com.
Re: [go-nuts] [Generics] Feedback on updated draft
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, : > > 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, i
Re: [go-nuts] [Generics] Feedback on updated draft
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.
Re: [go-nuts] [Generics] Feedback on updated draft
On Wed, Jul 22, 2020 at 5:12 PM Aleksey Tulinov wrote: > > Hmm. I must have read the previous version, but it probably was some > time ago. I have to say that i like the previous version more than the > current one. > > I definitely don't like this: > > type structField interface { > type struct { a int; x int }, > struct { b int; x float64 }, > struct { c int; x uint64 } > } > > If this is more readable than `require (a) { a.x++ }`, then i don't > even know what to say. My understanding of readability must be very > different. Agreed. But this is an extreme special case, only included in the design draft as an example. Personally I think it would be fine if generics in Go had no way to say "the type must be a struct with a numeric field x." > However the real point is not in a syntax, but in decoupling require > expression from constraint, so it's more generic and extensibleso to > speak. So maybe: > > type IncrementX interface { > require (a) { > evaluates(a.x) && evaluates(a.x++) >// where evaluates() is defined elsewhere like `require (x) { x }` > } > } > > And type requirement could be `require { type int, int8, int16, int32, > int64 }`? Even though this still doesn't look right to me. The more language constructs we add, the harder the language is to understand. The current design draft adds two language constructs: type parameters and type lists in interfaces. It adds one new name: comparable. Every new name, like "require" or "evaluates", carries a heavy cost in comprehensibility, as it becomes something that every user of Go needs to learn. > >The objection you mention to the approach used in the current draft is > >something like: what if Go changes such that operators (other than == and > >!=) apply to types other than primitive types? But we're in charge of Go; > > Yeah, sure. But there will be some code out there that depends on > existing constraints. If I understand this correctly, `Ordered` in the > current proposal does not define constraint that require operator `<`, > it defines a specific set of types and exploits a side effect that > operator '<' can be applied to every type from this set. This > introduces other side effects, for example, operator `==` is also > defined for all those types, so at least it defines what it wasn't > supposed to define. > > I'm not sure if this is OK or not, just saying, but if Go somehow > changes "interface" of types under `Ordered`, then I think `Ordered` > will indirectly change its meaning because it will inherit those > changes even if they are unrelated to operator `<` or ordering in > general. Just wanted to point out that side effects work both ways, > and if those side effects are already exploited in existing Go code, > then it might be harder to change other parts that at first sight are > completely unrelated to what is being changed. Hope this helps. All true. The question is whether it is likely to lead to problems. 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/CAOyqgcUoo42KdGYjOKRwZRtGtsYAdPE-GiLQyG-UGsbh8KwFcg%40mail.gmail.com.
Re: [go-nuts] [Generics] Feedback on updated draft
Hmm. I must have read the previous version, but it probably was some time ago. I have to say that i like the previous version more than the current one. I definitely don't like this: type structField interface { type struct { a int; x int }, struct { b int; x float64 }, struct { c int; x uint64 } } If this is more readable than `require (a) { a.x++ }`, then i don't even know what to say. My understanding of readability must be very different. I admit that there is a point in those concerns too, it's not immediately obvious what `require (a) { a.x++ }` is supposed to do. However the real point is not in a syntax, but in decoupling require expression from constraint, so it's more generic and extensibleso to speak. So maybe: type IncrementX interface { require (a) { evaluates(a.x) && evaluates(a.x++) // where evaluates() is defined elsewhere like `require (x) { x }` } } And type requirement could be `require { type int, int8, int16, int32, int64 }`? Even though this still doesn't look right to me. >The objection you mention to the approach used in the current draft is >something like: what if Go changes such that operators (other than == and !=) >apply to types other than primitive types? But we're in charge of Go; Yeah, sure. But there will be some code out there that depends on existing constraints. If I understand this correctly, `Ordered` in the current proposal does not define constraint that require operator `<`, it defines a specific set of types and exploits a side effect that operator '<' can be applied to every type from this set. This introduces other side effects, for example, operator `==` is also defined for all those types, so at least it defines what it wasn't supposed to define. I'm not sure if this is OK or not, just saying, but if Go somehow changes "interface" of types under `Ordered`, then I think `Ordered` will indirectly change its meaning because it will inherit those changes even if they are unrelated to operator `<` or ordering in general. Just wanted to point out that side effects work both ways, and if those side effects are already exploited in existing Go code, then it might be harder to change other parts that at first sight are completely unrelated to what is being changed. Hope this helps. But I dig the idea of releasing this in parts, i'll be keeping track of this, this is a much needed feature. Thank you for looking into this. чт, 23 июл. 2020 г. в 00:21, Ian Lance Taylor : > > On Wed, Jul 22, 2020 at 1:46 PM Aleksey Tulinov > wrote: > > > > I'm not really a language designer and i don't use Go extensively, so > > please take my words with a grain of salt. But I like Go and would > > like to use it, and I'd like to put my 2 cents into the jar. I'm sorry > > if this was already discussed, I checked the mailing list but didn't > > find this. > > > > I've read the updated draft (sorry i'm late) and the thing that really > > rubbed me the wrong way was this one: > > > > "We need a way to write a constraint that accepts only types that > > support <. In order to do that, we observe that, aside from two > > exceptions that we will discuss later, all the arithmetic, comparison, > > and logical operators defined by the language may only be used with > > types that are predeclared by the language, or with defined types > > whose underlying type is one of those predeclared types. That is, the > > operator < can only be used with a predeclared type such as int or > > float64, or a defined type whose underlying type is one of those > > types. Go does not permit using < with a composite type or with an > > arbitrary defined type." > > > > This is a good observation, but what if Go changes in the future and > > this observation is no longer true? > > > > I think I somewhat understand the underlying problem, interface is a > > concept from the Java world and in Java "everything is object", and > > classic interface is limited to describing methods, etc etc. But > > interface is a form of constraint, so it does make a lot of sense to > > use it as a constraint for generic types. However, I thought that > > intention was to describe a constraint on objects composition, but > > instead it describes constraints on object types. This doesn't feel > > quite right. > > > > "If C++ and Java are about type hierarchies and the taxonomy of types, > > Go is about composition." Am i right? > > > > Was it considered to decouple constraints into 1) constraints and 2) > > constraints requirement expression? Something like this (here and > > below everything is pseudocode): > > > > type comparable interface { > > require (a, b) { > > a < b > > } > > } > > > > When constraint is checked, this requirement can be rewritten as a > > function for a concrete type, for example, for interface{}: > > > > func comparableRequireInterface(a, b interface{}) { > > _ = (a < b) // this won't compile, as expected, there is no operator > > <, requirement isn't satisfied > > } >
Re: [go-nuts] [Generics] Feedback on updated draft
On Wed, Jul 22, 2020 at 1:46 PM Aleksey Tulinov wrote: > > I'm not really a language designer and i don't use Go extensively, so > please take my words with a grain of salt. But I like Go and would > like to use it, and I'd like to put my 2 cents into the jar. I'm sorry > if this was already discussed, I checked the mailing list but didn't > find this. > > I've read the updated draft (sorry i'm late) and the thing that really > rubbed me the wrong way was this one: > > "We need a way to write a constraint that accepts only types that > support <. In order to do that, we observe that, aside from two > exceptions that we will discuss later, all the arithmetic, comparison, > and logical operators defined by the language may only be used with > types that are predeclared by the language, or with defined types > whose underlying type is one of those predeclared types. That is, the > operator < can only be used with a predeclared type such as int or > float64, or a defined type whose underlying type is one of those > types. Go does not permit using < with a composite type or with an > arbitrary defined type." > > This is a good observation, but what if Go changes in the future and > this observation is no longer true? > > I think I somewhat understand the underlying problem, interface is a > concept from the Java world and in Java "everything is object", and > classic interface is limited to describing methods, etc etc. But > interface is a form of constraint, so it does make a lot of sense to > use it as a constraint for generic types. However, I thought that > intention was to describe a constraint on objects composition, but > instead it describes constraints on object types. This doesn't feel > quite right. > > "If C++ and Java are about type hierarchies and the taxonomy of types, > Go is about composition." Am i right? > > Was it considered to decouple constraints into 1) constraints and 2) > constraints requirement expression? Something like this (here and > below everything is pseudocode): > > type comparable interface { > require (a, b) { > a < b > } > } > > When constraint is checked, this requirement can be rewritten as a > function for a concrete type, for example, for interface{}: > > func comparableRequireInterface(a, b interface{}) { > _ = (a < b) // this won't compile, as expected, there is no operator > <, requirement isn't satisfied > } > > For struct{}: > > func comparableRequireStruct(a, b struct{}) { > _ = (a < b) // this won't compile either > } > > For numeric types, specifically for int: > > func comparableRequireInt(a, b int) { > _ = (a < b) // this will compile for other numeric types too > } > > Playground link: https://play.golang.org/p/vzk6l7zvBY_J > > Note that `require (a, b) { a == b }` will be satisfied for structs, > interfaces, numerics and strings. > > This may (or may not) eliminate the need for predeclared `comparable` > constraint and it might (or might not) answer this: "It's not clear > that we fully understand the use of composite types in type lists": > > type IncrementX interface { > require (a) { > a.x++ > } > } > > Which can be rewritten for a concrete type: > > type ConcreteStruct struct { > x float64 > } > > As > > func checkIncrementXConcreteStruct(a ConcreteStruct) { > a.x++ // note that it can not be rewritten to _ = (a.x++) > } > > With empty `struct{}` it will give a sensible error message: > "./prog.go:28:3: a.x undefined (type struct {} has no field or method > x)" and it will detect error when ++ can not be applied: > "./prog.go:24:5: invalid operation: a.x++ (non-numeric type string)" > > Does any of this make any sense? > > P.S. Yeah, it looks like C++, full disclosure, this is why I'm writing > this email, I thought maybe Go could borrow something from there. I > believe GCC 10+ can compile C++ concepts and there is a "playground" > available at godbolt.org. Thanks for the note. What you are describing has some similarities to the first contracts design draft, which you can read at https://go.googlesource.com/proposal/+/764389869b7ca634cb88d134ff8c4c9f67c0be9b/design/go2draft-contracts.md . People raised significant objections to the approach described there on the grounds of complexity. In particular, it's hard to look at the requirements and understand exactly what operations are permitted in the generic function body. For example, it's very tempting to say "if < is supported, then <= is supported". Or "if + is supported, then += is supported, and vice-versa." It's tedious to have to write your requires statement to list both "+" and "+=". But then there are quite a few such rules that have to be written down and understood. It's hard to find an approach that is both easy to understand and easy to use. In fact in general it's quite hard to understand. The objection you mention to the approach used in the current draft is something like: what if Go changes such that operators (other than == and !=) apply to types oth
[go-nuts] [Generics] Feedback on updated draft
Hi, I'm not really a language designer and i don't use Go extensively, so please take my words with a grain of salt. But I like Go and would like to use it, and I'd like to put my 2 cents into the jar. I'm sorry if this was already discussed, I checked the mailing list but didn't find this. I've read the updated draft (sorry i'm late) and the thing that really rubbed me the wrong way was this one: "We need a way to write a constraint that accepts only types that support <. In order to do that, we observe that, aside from two exceptions that we will discuss later, all the arithmetic, comparison, and logical operators defined by the language may only be used with types that are predeclared by the language, or with defined types whose underlying type is one of those predeclared types. That is, the operator < can only be used with a predeclared type such as int or float64, or a defined type whose underlying type is one of those types. Go does not permit using < with a composite type or with an arbitrary defined type." This is a good observation, but what if Go changes in the future and this observation is no longer true? I think I somewhat understand the underlying problem, interface is a concept from the Java world and in Java "everything is object", and classic interface is limited to describing methods, etc etc. But interface is a form of constraint, so it does make a lot of sense to use it as a constraint for generic types. However, I thought that intention was to describe a constraint on objects composition, but instead it describes constraints on object types. This doesn't feel quite right. "If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition." Am i right? Was it considered to decouple constraints into 1) constraints and 2) constraints requirement expression? Something like this (here and below everything is pseudocode): type comparable interface { require (a, b) { a < b } } When constraint is checked, this requirement can be rewritten as a function for a concrete type, for example, for interface{}: func comparableRequireInterface(a, b interface{}) { _ = (a < b) // this won't compile, as expected, there is no operator <, requirement isn't satisfied } For struct{}: func comparableRequireStruct(a, b struct{}) { _ = (a < b) // this won't compile either } For numeric types, specifically for int: func comparableRequireInt(a, b int) { _ = (a < b) // this will compile for other numeric types too } Playground link: https://play.golang.org/p/vzk6l7zvBY_J Note that `require (a, b) { a == b }` will be satisfied for structs, interfaces, numerics and strings. This may (or may not) eliminate the need for predeclared `comparable` constraint and it might (or might not) answer this: "It's not clear that we fully understand the use of composite types in type lists": type IncrementX interface { require (a) { a.x++ } } Which can be rewritten for a concrete type: type ConcreteStruct struct { x float64 } As func checkIncrementXConcreteStruct(a ConcreteStruct) { a.x++ // note that it can not be rewritten to _ = (a.x++) } With empty `struct{}` it will give a sensible error message: "./prog.go:28:3: a.x undefined (type struct {} has no field or method x)" and it will detect error when ++ can not be applied: "./prog.go:24:5: invalid operation: a.x++ (non-numeric type string)" Does any of this make any sense? P.S. Yeah, it looks like C++, full disclosure, this is why I'm writing this email, I thought maybe Go could borrow something from there. I believe GCC 10+ can compile C++ concepts and there is a "playground" available at godbolt.org. -- 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/CAMteYTbyeYKV55dLja%2BYqtGELA_4vh%3DnsvJRuaa6RUoTZ_Nx_A%40mail.gmail.com.