On Tue, Sep 11, 2018 at 1:54 PM Tristan Colgate <tcolg...@gmail.com> wrote:

> It feels like, so far, all of the attempts to simplify the original design
> wind up looking more complicated for little benefit (and reduced
> functionality).
>

There is no accounting for taste, of course, but I wildly disagree. In
particular, the constraint solver and type-checker for what I described is
trivial to write and understand, including good error messages. AIUI it is
currently not clear whether the contract design can actually be
type-checked in practice (and, personally, I am predicting the things I and
others are bringing up to be exactly the things that will come up as
difficultlies).


> Re-using interfaces seems like it would mandate boxing, (or interfaces are
> no longer interfaces as currently described, which would also be a major
> change).
>

That is not true. If interfaces are used to specify constraints, they
mandate boxing just as little or as much as contracts do. That should be
obvious in any case, given that you can always write an interface
constraint as a contract via

contract SomeInterfaceContract(v T) {
    var _ SomeInterface = v
}

In general, I don't believe there is any difference - the question of
boxing or not boxing is an implementation detail. Currently, there is
nothing in the spec that requires boxing to happen here:

func Foo(w io.Writer) { fmt.Fprintln(w, "Hello world") }
func main() { Foo(os.Stdout) }

The compiler could decide to devirtualize the interface, to generate a
boxed version or to do a hybrid approach (e.g. compile a boxed version and
devirtualize for certain types based on usage). Exactly like with the
contract designs.

Or, as I phrased it elsewhere: If a compiler can generate an instantiated
version of a parametric function Foo(type T) for a call Foo(v), it has to
be able to prove the type of v statically. In that case, it can also create
a devirtualized version of a hypothetical not parametric Foo(v interface{}).

I don't believe that there is any performance benefit to generics in the
general case, except where type-parameters and constraints can be used to
prove tighter bounds on the static types used - and in that case, it
doesn't matter how those constraints are expressed.

The original generic types and functions as described would permit unboxed,
> direct access to fields, without relying on inlining, which can't be
> achieved with the interface approach.
>

This is true, but misleading. If the compiler can generate code to do a
direct access of a struct field, it needs to know the static type of the
passed argument to know the offset of the field. In that case, it can also
trivially devirtualize and inline the access.
i.e. on the one hand, yes, it can't be done "without relying on inlining",
but to do it, you need to do the equivalent thing.

FWIW: In practice, the most likely implementation
<https://go.googlesource.com/proposal/+/master/design/go2draft-generics-overview.md#discussion-and-open-questions>
(look
for "dual implementation") for struct-field-accesses will be to pass an
implicit, autogenerated closure (or some equivalent data) which is pretty
much identical to using an interface.

In other words, I'm fairly sure that using interfaces to specify
constraints is also satisfying the dual implementation property outlined by
Russ.

Whilst contracts do introduce some ambiguities, it's not obvious that these
> are of genuine practical concern.
>

I would respond that, just as much, while interfaces+ as constraints do
lack some power that contracts can provide, it's not obvious that these are
practically useful things to express :)

Note that all the added power of contracts come from exactly the things
where ambiguities arise (namely builtin functions, statements like range
and some ambiguous operators). If the ambiguous situations won't come up in
practice, I contest that we need the power to express them. If they do, I'd
argue that we want them to be easy to understand and unambiguous.

Ian has pointed out before, this isn't really any different from types that
> coincidentally support an interfaces method set, sort.Sort will not do
> anything useful if you're types doesn't implement the correct semantic
> contract required. It's not obvious that contracts should attempt to be any
> more accurate.
>

I respectfully disagree (I think) with Ian that it doesn't matter. One of
the main reasons to introduce generics is to get type safe generics and
type safety is inherently about *excluding* invalid types. For example, it
is already possible to write a generic Max function
<https://play.golang.org/p/kDkgQ08iuhy> in Go. It is not type safe, though,
as we can all agree. And yet, the type of the function still obviously
allows all valid combinations of types it could be used for. But what makes
it lack type safety is the fact that it *also* allows type-combinations
that are invalid and panic at runtime.

So, to me, the point of type safety is to add power to the type system to
a) specify better, tighter bounds on valid input values and b) have the
compiler enforce those bounds. As such, it certainly matters whether or not
a given constraint specification allows types that are invalid.

I also *agree* with Ian, though, that there is a range of possible outcomes
and levels of type safety that make sense to aim for and I agree that it's
not clear that the level of safety the contracts design provides isn't "the
right tradeoff". It's certainly better than current Go and it is a little
bit (not hugely, but a little) worse than my design. I think it's "fine" in
terms of the level of safety it provides and I am certainly going to be
able to live with it, just as I am fine living with sort.Slice (in fact, I
am of the opinion that type-safety at all cost is bad
<https://blog.merovius.de/2017/09/12/diminishing-returns-of-static-typing.html>
).

I still believe that if there's a simpler alternative (and I consider my
design simpler, for the implementation, the author and the user of generic
code. But I'm obviously biased) that creates higher levels of type-safety,
while not giving up a significant amount of expressive power, then that
should be at least considered. Because it's just as much not obvious that
contracts provide the wrong level of type-safety, as it's obvious that they
provide the only correct level of type-safety.

TBH, I don't think I disagree with Ian as much as it might appear to
onlookers. I know him as a person who is super willing to accept and
consider feedback and disagree respectfully. So even if I don't sway him or
anyone, I do believe that he'll consider all sides and will have his
reasons to get to the conclusion he comes to. I just also wanted to make my
case :)

On Tue, 11 Sep 2018 at 12:40 Jonathan Amsterdam <jbamster...@gmail.com>
> wrote:
>
>> * I think constraining type-parameters to channel/slice/map types might
>>> not be needed at all. Instead of, say "func Foo(type T channelOfBar) (ch
>>> T)", ISTM that we can use "func Foo(ch chan Bar)". Since any channel-type
>>> with element type Bar can be assigned to `chan Bar`, this would already
>>> allow most of the useful patterns. As-is there is a slight reduction in
>>> power over the contracts design, because `contract channelOfBar(v T) { ch
>>> <- Bar{} }` also allows element types that Bar is assignable to.
>>> `assignableFrom(Bar)` would enable pseudo-interfaces to do that too. But
>>> IMO it would be helpful to see cases where that's needed (AFAICT none of
>>> the ones mentioned in the design doc or problem overview do).
>>>
>>
>> Some of the examples could be generalized. For instance, Copy(dest []T1,
>> src []T2) where T2 is assignable to T1.
>>
>> How about merging channels c1 and c2 into c3? You could write this as
>>
>>    func Merge(type T)(c1, c2, c3 chan T)
>>
>> but  the more general version is
>>
>>    func Merge(type T1, T2, T3)(c1 chan T1, c2 chan T2, c3 chan T3)
>>
>> where you can assign T1 and T2 to T3.
>>
>> --
>> 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.
>> 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 golang-nuts+unsubscr...@googlegroups.com.
> 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 golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to