What *exactly* is generics in Go?

blov · · 494 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I hear a lot of requests for generics in Go but to be honest, I&#39;m not exactly sure what that would mean if Go did have it. I come from a Ruby/JS background and I don&#39;t normally hear the term &#34;generics&#34; when learning those languages. I understand that Go is a statically typed language meaning that everything only has one type at runtime, even if it&#39;s an interface type. So far, I&#39;m managing with the static nature of Go using interfaces but it can be very limiting sometimes (having to type switch/convert a lot).</p> <p>Can someone explain to me what we can expect <em>if</em> Go actually had generics? Does that mean there will be a new &#34;generic&#34; type where I can access similar fields of two different structs? I&#39;m not looking to debate on the pros and cons on why Go should or shouldn&#39;t have generics. I just want to understand more about this topic and decide for myself if it actually is something that&#39;s needed or not.</p> <p>I&#39;d also appreciate if someone could explain with some Go pseudo-code, that would be very helpful, thank you!</p> <hr/>**评论:**<br/><br/>natefinch: <pre><p>In reality, what it does is replace the need for interfaces with specific types defined at compile-time.</p> <p>Where this comes in handy is when you need to support things that don&#39;t fit an interface (mostly built-in types, like the various numeric types and string).</p> <p>The other major advantage is that what you put in is what you get out.</p> <p>So, like, if you have a generic container in traditional Go, you use interface{}... which means when you put a value into it, it accepts anything (even if you really only want it to accept one type), and when you get a value out of it, you get interface{} and you need to convert to the concrete type (which could fail, makes your code slower, etc).</p> <p>With a true generic container, what you put in is what you get out, so like List&lt;Player&gt; would accept only Player values, and when you get a value out of it, it&#39;s always a Player.</p> <p>The main problem with generics is that they make your code harder to read, because you don&#39;t have real types in the generic code, you have some placeholder for the real type. Also, it tends to mean that you need a lot of awkward annotations on functions and types, like <code>func (list List&lt;X, Y&gt;) Get(x X) Y</code> .... where the stuff between the &lt; and &gt; are types used in that code (for example, int and string).. try reading a whole file full of that, and then imagine generics used inside other generics, like type List&lt;X, List&lt;X, Y&gt;&gt; (yes this happens <em>all the time</em> in languages with generics... so much so that it was a huge relief when C# finally stopped requiring a space between the &gt;&gt; to differentiate from the bit shift operator).</p> <p>It&#39;s kind of awful. Handy at times, but always awful. And that&#39;s why many of us are wary of adding generics to Go. We&#39;ve been burned by them in the past. I don&#39;t even care about compile times or code size or any of those other tradeoffs that people always bring up with generics. I care about code clarity. Generic code, as defined in other languages (such as C++, C#, and Java), is really really unclear.</p> <p>I hope there&#39;s a third way, that can enable some of the code that is really bothersome for Go devs right now (like custom containers, e.g. trees), without making the code horribly ugly and hard to understand.</p></pre>callcifer: <pre><p>I guess I&#39;m in the other camp then. I quite like the Java generics syntax (<code>List&lt;String, int&gt;</code> or even <code>List&lt;String, List&lt;String, String&gt;&gt;</code> looks perfectly readable to me) and it&#39;s quite close to what I&#39;d personally like to see in Go eventually.</p> <p>Let&#39;s hope the devs meet somewhere in the middle.</p></pre>DarkRye: <pre><p>Java&#39;s runtime erasure is a big deal. It is counterintuitive when more complicated cases are considered. .NET did it right. Generics are handy in .NET. But it is one of those tools that can be over used. It can also solve difficult real problems. I was glad to have generics in a few difficult cases. Most of the time I don&#39;t miss them. </p></pre>callcifer: <pre><blockquote> <p>Java&#39;s runtime erasure is a big deal.</p> </blockquote> <p>I know, it causes a lot of pain sometimes. What I meant above was purely for the syntax, since that&#39;s what the parent comment mentioned.</p> <p>What I&#39;m hoping for is an implementation that&#39;s at least close to the Java syntax, with much better compiler and runtime behaviour. Time will tell.</p></pre>SteveMcQwark: <pre><p>Yeah. As far as the core team is concerned, syntax is the least interesting aspect of the design, because it&#39;s basically arbitrary once you have the semantics and implementation nailed down. There is a high chance that Go would use square brackets (<code>List[T]</code>) instead of angle brackets (<code>List&lt;T&gt;</code>) because Go&#39;s type syntax currently parses consistently as part of the expression syntax, and angle brackets would be ambiguous (<code>f(List &lt; N, M &gt; (v))</code>; you can&#39;t know if you&#39;re parsing two values or one without resolving <code>List</code> first).</p></pre>callcifer: <pre><blockquote> <p>you can&#39;t know if you&#39;re parsing two values or one without resolving List first</p> </blockquote> <p>That could be solved with some lookahead support in the compiler, but it&#39;s a <em>very</em> slippery slope since that way lies C++ and hence madness...</p></pre>SteveMcQwark: <pre><p>Lookahead would be insufficient on its own in this case, unfortunately, since you <em>cannot</em> distinguish the two cases without doing name resolution first. To allow lookahead to work, you&#39;d have to require an awkward disambiguation syntax, like <code>(List&lt;N, M&gt;)(v)</code>, and the errors would be unpleasant. This can be so easily avoided just by using square brackets that it doesn&#39;t seem like it&#39;s even a question.</p></pre>metamatic: <pre><p>Where Java generics get horrendous in my experience is when you start to deal with things like <code>List&lt;X extends Comparable&lt;X&gt;&gt;</code>, <code>List&lt;? super List&lt;X extends Comparable&lt;X&gt;&gt;</code>, and having to remember the difference between <code>? extends Foo</code> and <code>T extends Foo</code> (and that for some reason it&#39;s always <code>extends</code> rather than <code>implements</code>, even if the class actually <code>implements</code> the interface).</p> <p>So you start to get things like this actual code:</p> <pre><code>public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) { list.sort(c); } </code></pre> <p>And then after all that hard work, all the information is lost at runtime anyway.</p> <p>Basically, Java generics add some compile-time error checking of simple situations without too much work -- but they make more complicated things horrendous.</p> <p>On the whole, I&#39;d rather have parametric polymorphism than generics.</p></pre>-knucklebones-: <pre><p>Generics can also be hugely confusing because of covariance and contravariance implications. I&#39;ve repeatedly painted myself into a corner in C# trying to implement an object hierarchy combining inheritance and generics. It&#39;s pretty frustrating to put in a ton of work only to find that your base class actually can&#39;t implement generics the way you intended and end up having to largely start over with the design.</p> <p>I think this is why functional languages and/or dynamic typing can be such a breath of fresh air. You&#39;re only worried about what something <em>does</em>, not what it <em>is</em>. Of course, this can lead to its own set of problems... </p></pre>wot-teh-phuck: <pre><p>Simply put, I think Go needs a way of eliminating duplicate code when implementing generic containers. It need not be the &#34;usual generics&#34; which comes with contravariance/covariance headaches but a simple type-safe template substitution (think C++ templates only far less powerful). This will remove the need for <code>IntContainer</code>, <code>StringContainer</code> and the likes...</p></pre>-knucklebones-: <pre><p>Yep - I think you&#39;re on the right track here.</p></pre>champioj: <pre><p>I think what you want is template package like defined <a href="https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/preview?pref=2&amp;pli=1#heading=h.wko1dvdznk4y" rel="nofollow">here</a>. It would really fit go very well and should be, in comparaison to &#34;full&#34; generic, way easier to implement.</p> <p>The problem is that going this way would completely remove the possibility to have more powerful generics in the future.</p></pre>jabbrwcky: <pre><p>What makes it even worse with Java is runtime type erasure. In the end it is just Object and you can not get any information via runtime introspection (with some exceptions that are awful to use).</p> <p>So I&#39; d rather stick with go&#39;s verbosity before trading for a half-done generics implementation like Java has it</p></pre>djk29a_: <pre><p>This legacy of runtime erasure leads to other JVM languages implementing things like tuples as concrete types such as in Scala&#39;s tuples and even function arity <a href="https://github.com/scala/scala/blob/2.12.x/src/library/scala/Tuple8.scala" rel="nofollow">https://github.com/scala/scala/blob/2.12.x/src/library/scala/Tuple8.scala</a> <a href="https://github.com/scala/scala/blob/2.12.x/src/library/scala/Function17.scala" rel="nofollow">https://github.com/scala/scala/blob/2.12.x/src/library/scala/Function17.scala</a></p> <p>Even with Clojure&#39;s compiler / standard library tuples gave up on the class system mostly and went with method overloading instead to avoid runtime information loss problems <a href="https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Tuple.java" rel="nofollow">https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Tuple.java</a></p></pre>jabbrwcky: <pre><p>A bit offtopic as not related to golang, but Idris and dependent types look tempting</p></pre>jonbonazza: <pre><p>Honestly, I&#39;d be perfectly happy if the following worked:</p> <pre><code>type T interface { Do(x interface{}) interface{} } type IntT struct {} func (s *S) Do(x interface{}) int { return 0 } </code></pre> <p>And IntT was considered an implementation of T by the compiler. 90% of the time, I don&#39;t care about generics, but 90% of the 10% of the time that I would like them, I&#39;d be 100% happy if only the return type of interface{} could be overloaded in the implementation. Java does this, and it&#39;s super handy at times, even outside of generics. I&#39;m usually okay with passing interface{} in as an argument as the code is mostly hidden from the end user (talking library code, here), but conveying return types to an end user is super important.</p> <p>Also, 97% of statistics are made up on the spot.</p></pre>realtalkintech2: <pre><p>I think its better to make a decision on it <strong>now</strong>. Even if it has downsides, because its just so high in demand. </p> <p>I&#39;d rather have poorly implemented generics that make golang look more like C++ or Java, than <strong>not have type safety as all</strong>.</p> <p>Also i think that golang should only be for <strong>computer scientists</strong> anyway. generics raises the bar and complexity of the language so only good programmers will use it.</p> <p>thats why Haskell is so popular in academia. as well as scala in twitter, linked in and growing fields. you have to have a higher mind, a deeper thinking one, to be able to understand the covariance, contravariance and von neuman states.</p> <p>when we get generics, golang may not be &#34;simple&#34; any more. but thats good. golang will be <strong>ready for industry</strong></p></pre>natefinch: <pre><p>Uh, I&#39;m not sure if you&#39;re joking or not.</p> <p>We do have type safety. If you have generic code that needs to work on multiple types, you just have to copy and paste it (or, preferably, generate it). Yes, that&#39;s not ideal. I think you&#39;re complaining about using interface{} for generic things... yeah, don&#39;t do that.</p> <blockquote> <p>Also i think that golang should only be for computer scientists anyway. generics raises the bar and complexity of the language so only good programmers will use it.</p> <p>when we get generics, golang may not be &#34;simple&#34; any more. but thats good. golang will be ready for industry</p> </blockquote> <p>LOL wut? Please tell me this is a joke. Go is being used in industry right now. Many major software companies have some code written in Go, and more are picking it up all the time. </p> <p>If a programmer wants something complicated like Haskell or Scala, Go is not the language for them. It never will be. Making it more academic will not make it more useful or better. I don&#39;t want to use a language that is so complicated that only a small number of people can use it. That limits who I can talk to about it and that limits my job opportunities.</p></pre>shovelpost: <pre><blockquote> <p>thats why Haskell is so popular in academia. as well as scala in twitter</p> </blockquote> <p>Meanwhile, gophers solve real industry problems (docker).</p></pre>PsyWolf: <pre><p>Ian Lance Taylor, of the go team, put together a mock proposal of what generics do, why they&#39;d be useful and what they might look like in go.</p> <p><a href="https://github.com/golang/proposal/blob/master/design/15292-generics.md">https://github.com/golang/proposal/blob/master/design/15292-generics.md</a></p></pre>Partageons: <pre><p>And here&#39;s a <a href="https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/preview?pref=2&amp;pli=1" rel="nofollow">thorough treatise</a>.</p></pre>jussij: <pre><blockquote> <p>I come from a Ruby/JS background</p> </blockquote> <p>I&#39;m not sure the classical definition of <em>Generics</em> applies to dynamic languages like Ruby/JS?</p> <p>I can only offer my experience of <em>Generics</em> to the language I used and that was C++ with it&#39;s introduction of templates (the first form of generics in C++).</p> <p>For simple things, templates did seem to work and work well.</p> <p>But as they where used for more and more complex coding constructs the compiler and linker errors became more and more obscure.</p> <p>I suspect it is that name mangling, unresolved rubbish that is so common in the modern generic C++/STL that by not embracing generics the designers of Go are trying to avoid.</p></pre>HectorJ: <pre><p>You can take a look at what Java has: <a href="http://www.tutorialspoint.com/java/java_generics.htm">http://www.tutorialspoint.com/java/java_generics.htm</a></p></pre>mc_hammerd: <pre><p>it can be two things.</p> <p><em>one code for two types</em></p> <p>in go you have to write <code>isZeroFloat64(val float64) and iszeroInt64(val int64)</code> even though the code is the same <code>return val == 0</code></p> <p>so with generics you could write:</p> <pre><code>func isZero(val TYPE){ return val == 0 } // TYPE is a keyword that means generic type </code></pre> <p>its kind of annoying not having it if you want to write a pop() function you have to write <code>popInt popint64 popint8 popint16 popfloat popfloat64 popbyte poprune popstring</code></p> <p>or <em>generic classes</em></p> <p>ex <code>List&lt;int&gt;</code> and <code>List&lt;float&gt;</code></p></pre>eldosoa: <pre><p>The first example does seem very convenient and simple. In contrast, I can understand how the second example can potentially &#34;open the floodgates&#34;, since if you create a generic list/collection type for ints and floats, where do we stop? </p></pre>dmikalova: <pre><p>That&#39;s the point - you don&#39;t stop.</p></pre>SomeCollegeBro: <pre><blockquote> <p>The first example does seem very convenient and simple. In contrast, I can understand how the second example can potentially &#34;open the floodgates&#34;, since if you create a generic list/collection type for ints and floats, where do we stop?</p> </blockquote> <p>I think you are misunderstanding - you only create the generic function or class once, then you can use it many times with any type you want. Here is a simple example in C++:</p> <pre><code>template &lt;class T&gt; T doSomething(T item) { return item + 5; } int testInt; float testFloat; doSomething(testInt); doSomething(testFloat); </code></pre> <p>Now, I can call &#34;doSomething&#34; with any type passed in (as long as it can use the + operator).</p> <p>This may sound pretty close to interfaces, the difference however is usually generics traditionaly are closer to code generation. For every time you use that function with a new type, the compiler will generate code specifically for that type.</p> <p>Currently, interfaces can solve that problem, however you need to write code for every type that you want to have an interface. Generics would solve this by (more or less) automatically generating code for every type needed.</p></pre>mvitorino: <pre><p>Without generics last line of code blows up at runtime:</p> <p><a href="https://play.golang.org/p/kVK5D2xdbY">https://play.golang.org/p/kVK5D2xdbY</a> </p> <p>With generics (example of what syntax could be) would not compile:</p> <p><a href="https://play.golang.org/p/DjViSFdCwe">https://play.golang.org/p/DjViSFdCwe</a></p></pre>SeerUD: <pre><p>For example:</p> <pre><code>greetings := []string{&#34;Hello&#34;, &#34;Goodbye&#34;} // messages will be []string{&#34;Hello, world!&#34;, &#34;Goodbye, world!&#34;} messages := Map[string](greetings, func(s string) string { return s + &#34;, world!&#34; }) // This could be safely implemented with generics with something like. // We call with the type T, so the compiler will be able to see if we&#39;re doing // something stupid here at compile-time. func Map[T](collection []T, fn func(T) T) []T { results := []T for _, item := range collection { results = append(results, fn(item)) } return results } // The alternative is writing one of these functions for each type, hence the // argument about having to write a lot of boilerplate code to get around the // lack of generics in Go. </code></pre> <p>Something like that anyway. There are far more applications for generics than just this kind of thing. But basically, it&#39;ll help you make more &#34;generic&#34; <em>things</em> that can be used with different types. </p></pre>brokedown: <pre><p>I&#39;m amused at how many people think Go sucks because it doesn&#39;t have generics.</p> <p>Use something else. Don&#39;t try to convince the Go team to fuck up the language to appease your want for your pet feature. Go does not have generics, yet somehow manages to be a popular, useful language. If you&#39;re so high up on your horse that you just can&#39;t use Go because of a lack of generics, that&#39;s just fine. The Generics discussion is frustrating because it&#39;s provably obvious that Go doesn&#39;t need them to be successful.</p></pre>kraml: <pre><p>There is a lot in the middle - people who use it despite a lack of generics, who see that a lot of their code would be a lot shorter and/or nicer if they had generics.</p></pre>generalT: <pre><p>there are two places in my production code that downright scare me because go doesn&#39;t have generics.</p> <p>that being said, i&#39;m moving all of the stuff i control from C# to go.</p></pre>brokedown: <pre><p>Sure, but at its core, those people either successfully wrote their software without generics in Go, or they had to use some other language. The net result, and provable fact, is that Go is successful in spite of it, making generics seem really really optional. Lots of other great languages have generics, but that certainly doesn&#39;t make them mandatory.</p> <p>Generics aren&#39;t a simple question with a simple answer. I encourage people to read this excellent <a href="https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit" rel="nofollow">summary of generics and go</a></p></pre>kraml: <pre><p>I am aware of the problems and that you can (and people obviously) do use Go successfully in business. I also use Go professionally every day. That does not mean that the language does not have a lot of shortcomings. The fact that you can use something successfully does not mean we should not try to improve it.</p></pre>eyecikjou567: <pre><p>Use Interfaces.</p> <p>IIRC Generics means a method can work with any type. I find this somewhat unsafe, especially if you are exposing something to the net.</p> <p>Rather if the method implements a interface, only compatible types are accepted and the maintainer of that method is not responsible for weird type fuckups by someone else.</p></pre>SteveMcQwark: <pre><p>It&#39;s the opposite. Generics allow you to restrict the interface of a function to a greater extent than interfaces on their own do. An interface only lets you specify the behaviour of a single value. Generics allow you to specify the relationships between values, such as that <em>this</em> value can be safely inserted into <em>that</em> slice or sent on <em>that</em> channel, or <em>these</em> values can be compared or added together, etc..</p></pre>eyecikjou567: <pre><p>I still don&#39;t think Generics in Go would do anything that you can&#39;t do with careful thought and interfaces.</p></pre>SteveMcQwark: <pre><p>If you&#39;re being clever, you can come up with some sort of type-safe stack machine in order to represent the data you need to operate on as a single value. But then you&#39;re writing code in an inefficient implementation of a stack-oriented language with awkward, error prone syntax, and implementing all the underlying infrastructure for each combination of types you need to use is still non-trivial.</p></pre>acron5n5: <pre><p>I think generics won&#39;t ever find their way in Go or they will not be of use. Go has its design and generics will not fit into it without redesigning (in fact recreating) basic Go concepts. In fact, go has one killing thing (its concurrency model, just in case you know nothing about Go). Without these magic goroutines it&#39;d be a language with an odd (not to say awful) syntax, smth like Pascal of XXI century or whatever and no one would actually use it. If one day there will be a language which shall offer the same performance and usability combined with all these sweet things (generics, lambdas, java-style collections etc) it&#39;ll kill Go in no time in my opinion.</p> <p>I don&#39;t know what everyone who&#39;s bagging for generics is looking for since Go isn&#39;t an object-oriented language and thus it&#39;d be a huge nonsense to see generics in it.</p> <p>In fact, most hard core gophers (among those who really like the language design, not just goroutines) whom I know don&#39;t want generics.</p></pre>realtalkintech2: <pre><p>Golang is a useless language in the sense it <strong>can&#39;t handle enterprise</strong>.</p> <p>I am very satisfied I use languages like Java and Haskell because we have <strong>radical support</strong> for generics.</p> <p>I hope golang learns the lessons of C++, Java and Rust.</p> <p>The more Golang immitates Rust and Haskell - the more correct code will be.</p> <p>Besides, who cares about all these child programmers? If your brain is <strong>too small</strong> for generics, then go do ruby or javascript.</p> <p>Golang needs to grow up. Get generics. Get with it go.</p></pre>funny_falcon: <pre><p><a href="https://github.com/golang/go/wiki/GoUsers" rel="nofollow">https://github.com/golang/go/wiki/GoUsers</a> Dropbox, CloudFlare, DigitalCloud, ... Perhaps it is not enterprise.</p></pre>mcouturier: <pre><p>and... Google?</p></pre>realtalkintech2: <pre><p>Golang needs generics, more complex type system, immutability by default etc.</p> <p>It should learn from Rust.</p> <p><a href="https://doc.rust-lang.org/book/mutability.html" rel="nofollow">https://doc.rust-lang.org/book/mutability.html</a></p></pre>izuriel: <pre><p>It sounds like you want to use Rust. So if your using Go and expect it to behave like Rust then I doubt it&#39;s the language that&#39;s flawed.</p> <p>Rust is a good language in its own right. If that&#39;s what you like then use it. Don&#39;t blindly demand every other language be Rust. </p> <p><strong>edit</strong> spelling</p></pre>acron5n5: <pre><p>@realtalkintech2 Than what would be the point of Go&#39;s existence? Haskell and Rust are already around, than why would one want to use Golang? People (and companies which is more important here) want something simpler than Haskell and effective at the same time and this is when languages like Go come in. You can for example call PHP coders children all day still PHP (and not haskell) is the most popular when it comes down to web development. P.S. by the way, Haskell is not production ready nor will it ever be that&#39;s why Go is used by more companies than Haskell, although the latter is 20+ years older.</p></pre>

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

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