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.

Reply via email to