Why no "extension" functions with receivers on types outside working package?

agolangf · · 485 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>It seems like it would be useful to allow for &#34;extension&#34; methods on types declared outside your package for convenience (rather than using local type aliases). I&#39;m curious why this is not supported?</p> <hr/>**评论:**<br/><br/>pursuit92: <pre><p>In addition to the compilation problems brought up, it effectively would make new methods defined in the imported package a breaking change because they could potentially conflict with a method that the library consumer defined.</p> <p>For example, if package &#34;a&#34; defines method &#34;Foo&#34; on type &#34;Bar&#34;, and package &#34;b&#34; defines a method &#34;Baz&#34; on &#34;a.Bar&#34;, if package &#34;a&#34; goes back and makes a method &#34;Baz&#34;, the compiler will throw a duplicate method error.</p> <p>This would make keeping backwards compatibility while adding new functionality more difficult.</p></pre>driusan: <pre><p>If you have a type defined in package A, and extend it in package B, what methods are available on that type in package C? Does it change based on if package C imports B? What is File1 imports B but File2 doesn&#39;t in package C? Does it change depending on the file? </p></pre>bigdubs: <pre><p>package a: type Foo struct {}</p> <p>package b: func (f *Foo) Bar() bool { return true } // package load side effect</p> <p>package c: func (f *Foo) Bar() string { return &#34;bar&#34; } // compile error on packages that ref both b &amp; c</p> <p>i can see how the above would be tricky; basically we avoid this with type names and function names generally today because the package name qualifies the type name. you would need a mechanism to differentiate them in order to load both b &amp; c.</p></pre>barsonme: <pre><p>The best way to do that:</p> <pre><code>package a type Foo struct{} package b import &#34;a&#34; type Foo { a.Foo } func (f Foo) Bar() {} </code></pre></pre>dsymonds: <pre><p>How do you get that compile error if package c doesn&#39;t import package b. The compiler only knows what happens in c.go and a.go.</p></pre>bigdubs: <pre><p>it would be raised in package d that references both b &amp; c</p></pre>dsymonds: <pre><p>So you&#39;d have a compile error for a different package? That sounds confusing, and liable to slow down builds.</p> <p>Further problems:</p> <p>What happens when you reflect on Foo? Do you see methods added in other packages? How does that reflection data get created (right now it happens when package a is compiled).</p> <p>Presumably the added methods won&#39;t have access to unexported parts of Foo. That seems like it&#39;ll be confusing too.</p> <p>What happens if Foo is embedded in a struct in package e? How does the compiler know that there will be other methods on Foo at the time it compiles e?</p></pre>bigdubs: <pre><p>As it stands now having descendent packages error on imports is something that happens (if the packages are named the same, for instance). It&#39;s annoying but something that is not without precedent and imho the benefit of being able to extend types with helper methods outweighs the potential pain. </p> <p>Reflection would be hard; assuming reflection happens at runtime, it would need to have the full method receiver information amended from other packages (i guess you would need to have a registry of types affected by a package or similar). I am not sure how static this is, and if the reflection metadata for types can be changed. My sense is the metadata available to package &#39;d&#39; in my example would be different than for packages a, b, or c, but this could still be done at compile time.</p> <p>I don&#39;t know how struct embedding works enough to know if this is feasible, that is to say, if calling an embedded method is as simple as calling a normal receiver (i.e. (T).Method(v))</p></pre>abcded1234234: <pre><blockquote> <p>imho the benefit of being able to extend types with helper methods outweighs the potential pain.</p> </blockquote> <p>IMHO the pain of using and understanding types with extension methods outweighs the minor inconvenience of occasionally using a function instead of a method ;)</p></pre>bigdubs: <pre><p>This(Is(Not(As(Easy(v))))</p> <p>As().This().Is()</p> <p>Extension methods are a priority in other languages (c# uses them to really nice effect), it is interesting to see the golang communities reaction though.</p></pre>abcded1234234: <pre><p>I cannot see how this reaction is specific to the Go community. Adding extension methods to the language is not an easy task (you were presented with many hard questions to answer) and the benefits are not clear.</p> <p>Try suggesting to add extension methods to Java or Python. I doubt the reaction will be different.</p></pre>bigdubs: <pre><p>I wasn&#39;t judging the reaction here, there were valid points raised. &#34;Interesting&#34; != &#34;Terrible&#34;.</p> <p>The only real blockers I&#39;ve seen raised are struct composition and reflection. Reflection could just ignore extension methods (they would be a very visible example of syntactic sugar). Struct composition would be harder but I don&#39;t see how it wouldn&#39;t be possible.</p></pre>weberc2: <pre><p>Yeah, the Go community isn&#39;t inclined to do things just because other languages do them. A lot of us like Go because it&#39;s such a simple language, and it&#39;s a simple language precisely because it <em>didn&#39;t</em> follow the bandwagon. In particular, you&#39;ll have a bad time writing Go if you&#39;re constantly trying to shoehorn other languages&#39; idioms into it--Go is not a very expressive language, as such there is often only one good way to do a particular thing. Depending on your priorities, this might be a good thing or a bad thing. Personally, I think having a uniform way to do things is a very good thing, even if it that way is not my preferred way to do a particular thing.</p> <blockquote> <p>As().This().Is()</p> </blockquote> <p>I&#39;m confused about what this has to do with extension methods. This is doable today in Go without extension methods; however, it&#39;s rarely used because Go methods frequently return multiple values.</p></pre>bigdubs: <pre><p>That stance, that <code>go doesn&#39;t follow the bandwagon</code> is also not necessarily true. There is a <code>range</code> keyword, there are plenty of other niceties from other languages. Simple ones that would lead to productivity gains for the developer shouldn&#39;t be ignored because they haven&#39;t been implemented yet right? </p> <blockquote> <p>As().This().Is()</p> </blockquote> <p>Is not really doable today on types you don&#39;t declare. You have to alias them first. </p></pre>SteveMcQwark: <pre><p>One way to add extension methods would be to add namespaced methods. Right now, all exported methods share a single namespace, so allowing for arbitrary extension of types with new methods would create all manner of potential conflicts.</p> <p>With namespaced methods, this wouldn&#39;t be a problem. Any package would be able to add exported methods to an external type provided that the methods are namespaced to the package. Here&#39;s a strawman syntax:</p> <pre><code>package pkg func (s string) package::DoThing() {} </code></pre> <p>This method is how namespaced to the package. Another package could do:</p> <pre><code>import &#34;pkg&#34; ... s.pkg::DoThing() </code></pre> <p>or</p> <pre><code>s.DoThing() </code></pre> <p>provided that no packages that define a conflicting method are imported. It could also define a namespaced method on a type it declared:</p> <pre><code>type MyType struct{} func (m MyType) pkg::DoThing() {} </code></pre> <p>allowing it to conform to a common interface.</p> <p>However, I don&#39;t see it as very likely that Go would adopt something like this. The core team are very suspicious of &#34;unnecessary&#34; additions, and, at least for now, the accepted solution is just to create a new type and add methods to that. Maybe if Go ever gets generics, a way to extend arbitrary types to work with a new generic abstraction will be in greater demand.</p></pre>dewey4iv: <pre><p>You mean interfaces?</p></pre>bigdubs: <pre><p>Potentially on interfaces as well but I meant on struct types. </p></pre>weberc2: <pre><blockquote> <p>It seems like it would be useful to allow for &#34;extension&#34; methods on types declared outside your package for convenience (rather than using local type aliases).</p> </blockquote> <p>It&#39;s not obvious to me why this would be useful? Could you elaborate on some use cases?</p></pre>bigdubs: <pre><p>One use case would be Map(), Filter(), FoldLeft etc. on slices or maps. </p> <p>The feedback that you can just write Map(slice []int, mapFn ...) is valid, however, and ultimately it seems like the work to get multiple packages adding receivers to a type remains prohibitive. </p></pre>bigdubs: <pre><p>As a meta commentary it is encouraging that someone just went through and down-voted every one of my comments.</p></pre>zacheryph: <pre><p>There are many problems with this. All quotes are OP.</p> <blockquote> <p>i can see how the above would be tricky .....</p> </blockquote> <p>One of Go&#39;s primary goals is to <em>NOT</em> be &#34;tricky&#34;.</p> <p>Adding to this, can you imagine seeing code working and trying it yourself and seeing it not work. Unknown that you need to import <em>github.com/completely/unrelated</em> for it to work.</p> <p>To add on to this issue is documentation. Now when I see code using pkg.Struct.SomeMethod I cannot assume to look at pkg&#39;s documentation for what SomeMethod does. I have to either have tooling to tell me where a method is defined, or look at the entire import list and search them all for where the method originates. This <em>potential pain</em> in itself I would say far outweighs any benefit of <em>syntactic sugar</em>.</p> <blockquote> <p>It&#39;s annoying but something that is not without precedent and imho the benefit of being able to extend types with helper methods outweighs the potential pain.</p> <p>I don&#39;t know how struct embedding works enough to know if this is feasible, that is to say, if calling an embedded method is as simple as calling a normal receiver (i.e. (T).Method(v))</p> </blockquote> <p>This alone shows how horrible this entire thread is and one of my peeves. You are weighing benefits and caveats and you don&#39;t know the language as it is. This is like a first year university student telling the professors they are doing everything wrong.</p> <blockquote> <p>Reflection could just ignore extension methods (they would be a very visible example of syntactic sugar).</p> </blockquote> <p>Why shouldn&#39;t the language as a whole [or we for that matter] just ignore extension methods in this case? Reflection is there for a purpose and you are saying <em>&#34;just ignore this stuff over here.&#34;</em> I think this could actually render it completely useless.</p> <p>structs and interfaces allow embedding. methods can be <em>&#34;overridden&#34;</em> or <em>&#34;added&#34;</em> this way. If you knew the language you would understand this. This is your <em>convenience</em> method you are asking for.</p> <p>If all you want is syntactic sugar for whatever is not already allowed, I see no reason why you can&#39;t do this now with <code>go generate</code>. You can even go as far as to call it your own language. Call it <em>go.ext</em> ;)</p> <p>Though I would personally say its better to learn the current benefits and caveats of a language before suggesting adding more.</p></pre>bigdubs: <pre><p>I write golang day to day, it&#39;s pretty dismissive to think I don&#39;t know the ins an the outs of the language because I don&#39;t claim to have full compiler internals knowledge of struct composition. I don&#39;t view this honesty as a thing to be attacked or puts me in a position where I can&#39;t start a discussion. That elitism is toxic.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

485 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传