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 <i...@golang.org>:
>
> On Wed, Jul 22, 2020 at 1:46 PM Aleksey Tulinov
> <aleksey.tuli...@gmail.com> 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 other than primitive types?  But we're in
> charge of Go; it's not like such a change would occur randomly or
> unpredictably.  It's worth considering whether we might decide to make
> such a change, and consider how the generics design draft might handle
> it.  But we don't have to avoid relying on this property (operators
> only apply to primitive types) because it might theoretically change
> in some way that we can't predict.
>
> For example, perhaps we would extend the ordering operators < <= >= >
> to work on non-primitive types, such as array types.  If we made that
> change, it would apply to all those operators consistently.  So we
> could extend generics to add a new builtin interface ordered, similar
> to comparable, and declare that any primitive type that implements the
> ordering operators will implement the ordered interface.
>
> Or, for example, we might introduce operator methods, such that a type
> could define a < operator for itself, as can be done in C++ and other
> languages.  If we made that change, we would certainly want to make it
> a method, and we would want to be able to declare an interface type
> that could accept any type with a < method.  So for generics we would
> be able to use that same syntax in type constraints.
>
> I haven't been able to think of any other plausible way that we might
> extend operators to work on non-primitive types, so I'm not concerned
> about that possibility.  Of course it's true that this will limit some
> possible future language changes.  But Go is not intended to be a
> language with many new features being added on a regular basis.  It's
> hard to be too concerned about limiting the possibility of adopting
> changes that nobody has suggested.
>
> Thanks again.
>
> 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/CAMteYTa200MNT8SbVuJokTd1AMvcFa9vX_kmAfOqaFrTXqGLmg%40mail.gmail.com.

Reply via email to