I suggest we move the discussion about Axel's post to reddit <https://www.reddit.com/r/golang/comments/husc75/parametric_context/>. Let's keep this exclusively on the the topic of embeding + generics. For anyone joining here, the main arguments are in the 4th post here <https://groups.google.com/g/golang-nuts/c/v10JCQFm9_E/m/GeaWpDn-AAAJ>. On Tuesday, July 21, 2020 at 1:45:35 PM UTC+2 ren...@ix.netcom.com wrote:
> The Context problem is more easily solved using package namespaces for > keys with conventions rather than type embedding. > > Otherwise you end up with a brittle multiple inheritance problem. > > Or you need language support for typed TLS and remove Context entirely. > > On Jul 21, 2020, at 6:24 AM, 'Axel Wagner' via golang-nuts < > golan...@googlegroups.com> wrote: > > > > On Tue, Jul 21, 2020 at 10:54 AM 'Javier Zunzunegui' via golang-nuts < > golan...@googlegroups.com> wrote: > >> It's not A(B,C,D) vs A(B(C(D))), both are supported with & without >> embedding. My issue is with a linked list that requires a new type for each >> additional node. It is potentially an explosion of types > > > I don't think "explosion" is an appropriate word. It's really not any > worse than regular embedding. You can write `x := struct{ Y }{y}` just > fine, and you wouldn't say that embedding leads to "an explosion of types", > right? And `context.Value` also needs a new type for each value stored in > it, if used as recommended (for each key), and there's no "explosion of > types" there either. > > I can't think of a way you can make the number of types in a program grow > superlinear in code-size if we allow embedding (that we can't already do), > so I really don't think "explosion" is the right word - and that's > important, because it's indicative of there not really being a problem with > that > > an imho an abuse of the type system. > > > Potato, Tomato :) There are loads of things that are allowed and even > often recommended, that could very well be called an "abuse of the type > system". Like defining an unexported type and using it as an argument, to > enforce that only literals can be used. Or adding _-fields to prevent > comparison or copying of structs. > > I agree that it's probably too clever for its own good and I would prefer > if it were simpler. But I do think it would be very useful to have a way to > amend an arbitrary type with new methods without erasing any of the > existing ones. Context.Value was only the first example I wrote about. But > there are others as well, like `http.ResponseWriter`. Even an `io.Writer` > may benefit from being able to be wrapped without erasing potential > `WriteByte`, `WriteRune`, `ReadFrom`… methods. > > Embedding type-parameters is a way to achieve that and I don't know a > different way. > I'm not saying a generics design *must* enable it, but it's certainly a > useful feature to have. > > >> Agreed. My point is it either ads no value, or when it does add some it >> is probably at the expense of things like maintainability, abstraction, >> tooling, types system, etc. and is simply not worth it. >> > > But that's a *very different* point from your original "The fundamental > problem I see is it introduces complexity, but no value". Now you're simply > saying "it's a tradeoff and I don't think it's worth it", which is fine of > course :) > > I don't think I agree, personally. IMO it is very natural being able to do > this and easy enough to implement, so I don't really think the cost is > *that* high. And if it was *just* to do my context-experiment, I'd agree > that the benefit is probably too low. But as I said, a) I don't think > that's the only use-case and b) people *are* frequently complaining about > the lack of type-safety of Context.Value, so there is definitely interest > to solve that problem *somehow*. > > But ultimately, yeah. It's a tradeoff, whatever the outcome is, I'll be > fine :) > > PS I didn't find where to discuss your post in your blog. If you have >> somewhere for that please share and I'd be happy to discuss at more length >> there >> > > I don't have a dedicated comment-section, because I don't want to > moderate. You can discuss via off-list E-Mail if you want and I also always > submit my posts to /r/golang > <https://www.reddit.com/r/golang/comments/husc75/parametric_context/>. > > > On Tuesday, July 21, 2020 at 10:15:58 AM UTC+2 axel.wa...@googlemail.com >> wrote: >> >>> Why do you feel that a type A(B, C, D, E) should be supported, but >>> A(B(C(D(E))) shouldn't? They are equivalent in expressiveness. And to be >>> clear, I'd also prefer A(B, C, D, E) for this use, but ISTM this isn't >>> possible without variadic type-parameters (i.e. I'd need a way to express >>> "this function takes a A(…) and returns an A(B,…)"). So to get this value, >>> you'd either need embedded type-parameters or variadic type-parameters. >>> >>> At the end of the day, though, my main point was "there is value". You >>> may very well feel that the value isn't worth the cost (as I say in the >>> conclusion, I'm not sure how I feel about that myself). But would you agree >>> that your statement "it adds complexity without adding value" doesn't >>> really hold up? >>> >>> On Tue, Jul 21, 2020 at 9:46 AM 'Javier Zunzunegui' via golang-nuts < >>> golan...@googlegroups.com> wrote: >>> >>>> >>>> Thanks for sharing Axel. >>>> >>>> I would place your post firmly in the category "doing something wild >>>> and can't use generics for it". >>>> >>>> Philosophical issues around context aside, it implements the chain of >>>> contexts directly via the type system, e.g.the TYPE after N wraps is >>>> `FooContext(BarContext(... N times ... (context.Context))` >>>> I would say that is abusing the type system and don't think it shuld be >>>> something generics should aim to support. >>>> On Monday, July 20, 2020 at 9:54:57 PM UTC+2 axel.wa...@googlemail.com >>>> wrote: >>>> >>>>> Hi, >>>>> >>>>> given the discussion here, the blog post I just published might be >>>>> interesting: >>>>> https://blog.merovius.de/2020/07/20/parametric-context.html >>>>> To the best of my knowledge, what I describe there is only really >>>>> doable using embedded type-parameters. As I state in the conclusion, I'm >>>>> not entirely sure this would be something *good*. But I do think it adds >>>>> more type-safety and that does provide value. >>>>> >>>>> Interested to hear your thoughts :) >>>>> >>>>> Axel >>>>> >>>>> >>>>> On Mon, Jul 20, 2020 at 3:37 PM 'Javier Zunzunegui' via golang-nuts < >>>>> golan...@googlegroups.com> wrote: >>>>> >>>>>> Coming back at this after more thought & exploration. The fundamental >>>>>> problem I see is it introduces complexity, but no value. >>>>>> >>>>>> Say >>>>>> `type I interface {...}` >>>>>> `type S(type T I) struct {T}` >>>>>> Then the generic S (before instantiating with a particular T) may >>>>>> implement any interface, e.g. in any method on S or any other generic >>>>>> code >>>>>> using S, you may have >>>>>> `_, ok := (interface{}(S(...){})` ).(Whatever)` >>>>>> and the result of ok depends on what T is used. Without embedding, >>>>>> that is strue of T but not S. >>>>>> >>>>>> # What value did this add? >>>>>> - You can call methods listed in I directly, e.g. if method Foo is in >>>>>> listed I, you can do s.Foo(). >>>>>> => without embedding using s.I.Foo() is hardly a change. >>>>>> - You can implement interfaces based on method listed in I directly, >>>>>> e.g. if method Foo is listed in I and characterises interface Fooer, you >>>>>> can do Fooer(s). >>>>>> => without embedding, you can do Fooer(s.I) or if you actually >>>>>> want s in the interface, write a generic method Foo in S and keep >>>>>> Fooer(s). >>>>>> - You can implement interfaces based on method NOT listed in I >>>>>> directly, e.g. if method Foo is NOT listed in I, but is defined in some >>>>>> T >>>>>> and characterises interface Fooer, you can do Fooer(s) (for s := >>>>>> S(T){...}). >>>>>> => without embedding, you can't do this. You can do Fooer(s.I), >>>>>> or create a new type for this specificT, `type X S(T)`, and implement >>>>>> method Foo, but let's assume that neither is acceptable. How realistic / >>>>>> in >>>>>> what circumstances do you want this? If you require a struct to have a >>>>>> method not listed in any contract but present in the type that happens >>>>>> to >>>>>> be embedded on this instance of the generic, and want it to be assigned >>>>>> to >>>>>> an interface that uses that method but won't accept either the embedded >>>>>> field in the interface, nor a separate type derived from it with the >>>>>> additional method, I'd say you need to re-evaluate your requirements >>>>>> because you are not following any moderatly sensible standards, or at >>>>>> the >>>>>> very least accept you are doing something wild and can't use generics >>>>>> for >>>>>> it. >>>>>> >>>>>> # What did this cost? >>>>>> - Complexity => the possibility of there being more methods available >>>>>> in a struct than listed in its definition is in itself one more thing to >>>>>> think about. >>>>>> - Refactoring => an embedding can't be refactored without a breacking >>>>>> change (can't change `type S(type T I) struct {T}` to `type S(type T I) >>>>>> struct {T}` + methods in I), since users may be relying on methods not >>>>>> present in I. >>>>>> - Limits to Tooling: without embedding, `_, ok := >>>>>> (interface{}(S(...){})` ).(Whatever)` above may always be false and the >>>>>> tooling may be able to identify and flag that, with embedding there is >>>>>> nothing it can do since it may always be true. >>>>>> >>>>>> # No arbitrary restrictions >>>>>> "our hope is to make generic types work as much like ordinary types >>>>>> as possible, without arbitrary restrictions" >>>>>> => this is indeed a restriction, and I don't know how much that >>>>>> weights in the go team's mind regarding future features, etc. But on the >>>>>> other hand even allowing embedding in generics isn't quite as >>>>>> pre-generics >>>>>> go either, after all you are embedding using the generic's type name (in >>>>>> my >>>>>> example, T) and not the name of the actuall type T is instantiated with. >>>>>> In >>>>>> other words, the type produced can't be replicated without generics. >>>>>> Banning embedding you limit slightly the types you can produce, but at >>>>>> least those that are allowed are valid pre-generic go types. >>>>>> >>>>>> And of course there is also a 'delay decision' argument here: if >>>>>> embedding is banned, it can be added later. If it is allowed, the >>>>>> decision >>>>>> is final. >>>>>> >>>>>> I do not claim anything dramatic about this feature's significance, >>>>>> like generics with fail if embedding is allowed, but also generics will >>>>>> not >>>>>> fail because they weren't. So if there is no strong reason for, and >>>>>> there >>>>>> are some reasons against, it seems to me the best decision is to ban at >>>>>> first and re-evaluate later. >>>>>> On Tuesday, July 14, 2020 at 11:41:42 AM UTC+2 Javier Zunzunegui >>>>>> wrote: >>>>>> >>>>>>> Issue openned in https://github.com/golang/go/issues/40199. >>>>>>> >>>>>>> Still clarifying my thoughts on embedding types within generics, >>>>>>> will postpone this debate while I focus on other parts of the proposal >>>>>>> and >>>>>>> gain some more experience using the go2 branch. Not calling for any >>>>>>> action >>>>>>> here at this point. Thanks Ian. >>>>>>> On Monday, July 13, 2020 at 7:03:35 PM UTC+2 Ian Lance Taylor wrote: >>>>>>> >>>>>>>> On Mon, Jul 13, 2020 at 9:15 AM 'Javier Zunzunegui' via golang-nuts >>>>>>>> <golan...@googlegroups.com> wrote: >>>>>>>> > >>>>>>>> > In the context of Type Parameters - Draft Design#generic-types: >>>>>>>> > >>>>>>>> > Type embedding is allowed in the proposed generic changes. Oddly, >>>>>>>> the current implementation allows for calling methods in the embedded >>>>>>>> types >>>>>>>> but not using the method to satisfy an interface (here). That seems >>>>>>>> like a >>>>>>>> bug in the implementation. >>>>>>>> > >>>>>>>> > More significantly, it allows for calling not just the methods >>>>>>>> declared in the interface, but also methods available to the >>>>>>>> instantiated >>>>>>>> type but not part of the interface. Example here. I can't see what is >>>>>>>> the >>>>>>>> gain in allowing that, and it presents a risk as the interfaces >>>>>>>> satisfied >>>>>>>> by types originating from the same generic type will differ not only >>>>>>>> in the >>>>>>>> types of the methods, but also i the method names themselves. >>>>>>>> > >>>>>>>> > Would it not be best to not allow embeding in generic types? >>>>>>>> >>>>>>>> At least at first glance these just seem like bugs in the current >>>>>>>> implementation, not in the design draft as such. Would you mind >>>>>>>> opening an issue for these problems? Thanks. >>>>>>>> >>>>>>>> We could ban embedding type parameters, but our hope is to make >>>>>>>> generic types work as much like ordinary types as possible, without >>>>>>>> arbitrary restrictions. The existence of bugs doesn't in itself >>>>>>>> seem >>>>>>>> like a good enough reason to ban the feature. >>>>>>>> >>>>>>>> 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...@googlegroups.com. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/golang-nuts/36bae6eb-4537-4796-8c11-60e828a36d51n%40googlegroups.com >>>>>> >>>>>> <https://groups.google.com/d/msgid/golang-nuts/36bae6eb-4537-4796-8c11-60e828a36d51n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>> -- >>>> 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. >>>> >>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/082b29da-91ca-431f-9378-5b858727ddd3n%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/golang-nuts/082b29da-91ca-431f-9378-5b858727ddd3n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >> 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. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/0a37c712-e83b-45c9-9910-12d9b40d9566n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/0a37c712-e83b-45c9-9910-12d9b40d9566n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- > 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. > > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHOAcmOG6Vrhn1DDYRjz%2B_KXoxqJ9Jzom65pgJxn%2BdU_g%40mail.gmail.com > > <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHOAcmOG6Vrhn1DDYRjz%2B_KXoxqJ9Jzom65pgJxn%2BdU_g%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > > -- 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/3f4c1af9-e83d-4382-a41c-089a3eaf02ffn%40googlegroups.com.