--
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...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
I prefer seeing the contract by example over having a combination of two dozens of interface names like Eq, Lesser, Adder, Muler, Convertible(x), Ranger, Lener, Caper, ... that have to be mentally mapped to their actual syntactic representation. This smells like taxonomy ("the lowest form of academic work" ;)
People may find this excellent paper “Datatype-Generic Programming” by Jeremy Gibbons useful in this discussion. It’s 72 pages long. https://www.cs.ox.ac.uk/jeremy.gibbons/publications/dgp.pdfI want generics but I still don’t know if I like this go2 generics proposal. At least as an exercise it may be worth reimplementing go1’s builtin generics using the proposed features (not syntax but functionality). Plus standard generic collections including things like N dim. vectors. Hopefully this extended exercise would make it clear if the goal of making it easier to write correct code is better met with this proposal.
On Sep 2, 2018, at 3:19 PM, roger peppe <rogp...@gmail.com> wrote:On Sun, 2 Sep 2018, 5:09 pm Bakul Shah, <ba...@bitblocks.com> wrote:People may find this excellent paper “Datatype-Generic Programming” by Jeremy Gibbons useful in this discussion. It’s 72 pages long. https://www.cs.ox.ac.uk/jeremy.gibbons/publications/dgp.pdfI want generics but I still don’t know if I like this go2 generics proposal. At least as an exercise it may be worth reimplementing go1’s builtin generics using the proposed features (not syntax but functionality). Plus standard generic collections including things like N dim. vectors. Hopefully this extended exercise would make it clear if the goal of making it easier to write correct code is better met with this proposal.Writing standard generic collections seems like a worthy goal but N-dimensional vectors seem to me like they might not fall within scope, as a large part of getting those right is making them suitably syntactically convenient to express, and that doesn't sound like a core generics goal.
My five cents:1) the methods of the type template are defined by interface style2) operators are retrieved implicitly from function body3) function-calls inside are also retrieved implicitly from the function body
If we try to translate contracts into interfaces, the first thing we run into is that there's no way to refer to the dynamic type of the interface. Compare:
contract Fooer(t T) {
interface{ Foo() T }(t)
}
to
type Fooer interface {
Foo() ???
}
There would have to be some sort of self type:
type Fooer interface {
Foo() self
}
Let's say there's a built in convertibleTo constraint
type FooConvertible interface {
convertibleTo(int)
Foo() self
}
For one thing, this looks like a method. (Aside: This is a problem in the existing generics proposal as well if generic interfaces are allowed, since interface embedding would become ambiguous.) Presuming that we solve this ambiguity, there's a deeper challenge. If I have a generic interface:
type Taker(type T) interface {
Take() T
}
In general, a given concrete type could only satisfy this interface one way. Having
type IntAndFloatTaker interface {
Taker(type int)
Taker(type float)
}
wouldn't work because the same type can't implement both func (...) Take() int and func (...) Take() float. convertibleTo (and convertibleFrom) would be unique in this regard, and could get in the way of (say) being able to infer the type parameters of
func Take(type T, U Take)(t T) U { t.Take() }
T can be inferred from the argument, and you ought to be able to infer U from T, since T can only implement Taker(type U) for exactly one U. But, if convertibleTo exists, this isn't true in general.
This doesn't address operators. You could have builtin constraints for each operator, or each set of operators. Or you could have some sort of builtin method corresponding to each operator. These are certainly doable, but designing and naming the constraints/methods is a whole undertaking which the contracts draft seeks to avoid.
--
If we try to translate contracts into interfaces, the first thing we run into is that there's no way to refer to the dynamic type of the interface. Compare:
contract Fooer(t T) {
interface{ Foo() T }(t)
}
to
type Fooer interface {
Foo() ???
}
There would have to be some sort of self type:
type Fooer interface {
Foo() self
}
Let's say there's a built in convertibleTo constraint
type FooConvertible interface {
convertibleTo(int)
Foo() self
}
For one thing, this looks like a method. (Aside: This is a problem in the existing generics proposal as well if generic interfaces are allowed, since interface embedding would become ambiguous.) Presuming that we solve this ambiguity, there's a deeper challenge. If I have a generic interface:
type Taker(type T) interface {
Take() T
}
In general, a given concrete type could only satisfy this interface one way. Having
type IntAndFloatTaker interface {
Taker(type int)
Taker(type float)
}
wouldn't work because the same type can't implement both func (...) Take() int and func (...) Take() float. convertibleTo (and convertibleFrom) would be unique in this regard, and could get in the way of (say) being able to infer the type parameters of
func Take(type T, U Take)(t T) U { t.Take() }
T can be inferred from the argument, and you ought to be able to infer U from T, since T can only implement Taker(type U) for exactly one U. But, if convertibleTo exists, this isn't true in general.
This doesn't address operators. You could have builtin constraints for each operator, or each set of operators. Or you could have some sort of builtin method corresponding to each operator. These are certainly doable, but designing and naming the constraints/methods is a whole undertaking which the contracts draft seeks to avoid.
I was assume that generic interfaces are allowed, which we do not need "self" type.
This is not equivalent. If you have an interface defined as
type Fooer(type T) interface {
Foo() T
}
this corresponds to a contract written as
contract Fooer(s S, t T) {
interface{ Foo() T }(s)
}
You have two separate type parameters when you're really just trying to talk about a single type. You had to write it like this because your language is poorly designed. When using the interface, you'd always have to write
func DoFoo(type T Fooer(T)) ...
Requiring a generic trait to be used reflexively just so you can talk about operations involving multiple values with the same type isn't a good design. If would be like saying "we don't have to be able to name the receiver variable inside a method because we can always pass the receiver value as a separate parameter, like v.String(v)!". This is technically true, but if your language is designed like this, I don't want to use it. Requiring type constraints to be written like this is the same. You can't use the unfamiliarity of a new feature to justify making decisions that would be clearly bad ones in a more familiar context.
In terms of convertableTo, it can be put into the operator category, which will be implicitly retrieved from the function body.
"Can" and "should" are two different things. Assume when reading my previous and current response that I'm talking about a design where all operators and conversions supported by a type parameter have to be specified by its contract/interface constraint and can't be implied by the body of the function. Having only a partially specified interface makes it very easy to accidentally change the interface of your function by, for example, calling a function which directly or indirectly performs an operation not previously present in your code.
In general, a given concrete type could only satisfy this interface one way. Havingtype IntAndFloatTaker interface {Taker(type int)Taker(type float)}
I think this is still not allowed, as they have the same function name while different types.
That's my point. I'm saying that this could be a useful property. Given a parameterized interface I and a type parameter T that implements I(U), there can only be exactly one value of U for a given T. This could be used to assist type inference. If an interface can contain conversions, however, then there are multiple ways the same type can satisfy that interface—since a type can support conversions to multiple different types—which means this property wouldn't hold. I know you don't think conversions should go in interfaces/contracts, but I disagree, so here we are. This property also wouldn't hold if generic methods were added to the language. Hypothetically, if you could have
type Setter(type T) interface {
Set(T)
}
type S struct{}
func (s S) Set(type T C)(t T) { ... }
and if this allowed S to satisfy Setter(T) for any type T that satisfies C, then the above property wouldn't hold. The current proposal doesn't suggest this, and I think it's probably incompatible with the design goal of being implementation agnostic, but it would create a problem if this functionality were ever enabled in the future and the previous behaviour had been used to inform type inference. The solution used by other languages to solve this dilemma is to have type aliases in their equivalent of interfaces:
type Setter interface {
type T
Set(T)
}
type S struct{}
func (s S) Set(type T C)(t T) { ... }
// This would fail to compile because the compiler can't
// infer the identity of `Setter.T` for `S` based on
// `S.Set`, since the method can work for any type `T`
// that satisfies `C`:
//
// var _ Setter = S{}
//
Note that my intent here is just to explore the design space. I'm not sure whether this is a direction the Go team would be willing to go.
My five cents:1) the methods of the type template are defined by interface style2) operators are retrieved implicitly from function body3) function-calls inside are also retrieved implicitly from the function bodyFor graph example, we may declare it as:type Edgeser(type E) interface {Edges() []T}type Nodeser(type N} interface {Nodes() from, to N}type Graph(type Node Edgers(Edge), type Edge Nodeser(Node)) struct { ... }
For example there could be an eq interface that’s equivalent to a contract with an x == x.
The problem is that the spec says that the operands of == have to be the same type
--
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...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.