On Tuesday, September 4, 2018 at 6:07:32 PM UTC-4, xingtao zhao wrote:
> 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. Having
>
>
>
> type 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.
On Tuesday, September 4, 2018 at 6:07:32 PM UTC-4, xingtao zhao wrote:
>
>
>
> On Tuesday, September 4, 2018 at 2:26:58 PM UTC-7, Steven Blenkinsop wrote:
>>
>> 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
>>
>> }
>>
>>
>>
>
> I was assume that generic interfaces are allowed, which we do not need
> "self" type. In terms of convertableTo, it can be put into the operator
> category, which will be implicitly retrieved from the function body.
>
>
>> In general, a given concrete type could only satisfy this interface one
>> way. Having
>>
>>
>>
>> type 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.
>
>
>>
>>
>> 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.
>>
>
> As we do not have operator overloading, there are only a few sets of
> operations for each type, for example Numeric, Addable, Hashable, Equality,
> etc, and they can never be changed or extended. These are completely
> hiden/invisible from user as operator constraints are retrieved implicitly.
> So we do not have to name them.
>
> The only thing a contract can do while interface can not, is that we can
> not have constraints on structure fiels, as only methods are allowed in
> interface.
>
>
>>
>> On Tue, Sep 4, 2018 at 12:52 PM xingtao zhao <[email protected]> wrote:
>>
>>> My five cents:
>>>
>>> 1) the methods of the type template are defined by interface style
>>> 2) operators are retrieved implicitly from function body
>>> 3) function-calls inside are also retrieved implicitly from the function
>>> body
>>>
>>> For 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 { ...
>>> }
>>>
>>>
>>> On Monday, September 3, 2018 at 4:19:59 PM UTC-7, Ian Lance Taylor wrote:
>>>
>>>> On Sun, Sep 2, 2018 at 1:08 AM, 'Charlton Trezevant' via golang-nuts
>>>> <[email protected]> wrote:
>>>> >
>>>> > Link: [Getting specific about generics, by Emily
>>>> > Maier](https://emilymaier.net/words/getting-specific-about-generics/)
>>>>
>>>> >
>>>> > The interface-based alternative to contracts seems like such a
>>>> natural fit-
>>>> > It’s simple, straightforward, and pragmatic. I value those aspects of
>>>> Go’s
>>>> > philosophy and consider them to be features of the language, so it’s
>>>> > encouraging to see a solution that suits them so well. The author
>>>> also does
>>>> > a great job of contextualizing the use cases and debate around
>>>> generics,
>>>> > which I found incredibly helpful.
>>>> >
>>>> > Any thoughts?
>>>>
>>>> It's a very nice writeup.
>>>>
>>>> It's worth asking how the graph example in the design draft would be
>>>> implemented in an interface-based implementation. Also the syntactic
>>>> issues are real.
>>>>
>>>> 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 [email protected].
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.