9 ms·
The mess that is the current Java type system makes me skeptical when proponents of languages like Go say not to worry, that generics can be added later. If Ja
by mcculley 8y ago
The mess that is the current Java type system makes me skeptical when proponents of languages like Go say not to worry, that generics can be added later.
If Java had generics in the beginning, it would look a lot different and the type system would be more powerful and safe.
- cmiles74 8y agoI agree that generics should probably be a strong consideration right from the beginning. I disagree that this would mean that it would necessarily be done correctly or that it's impossible to add it later. That was the case with .Net and C#, it wasn't until the 2.0 release that generics were added. [0] [0]: http://mattwarren.org/2018/03/02/How-generics-were-added-to-.NET/ http://mattwarren.org/2018/03/02/How-generics-were-added-to-...
- monocasa 8y agoUnlike Java, Go doesn't have to worry about bytecode backwards compatibility. That was the main issue that had them reaching for type erasure. Take a look at C# which added it without many issues (because they were willing to break back compat of their bytecode). Go fits this model a whole lot better.
- enitihas 8y agoDid C# really break backwards compatibility of their bytecode? What I understand is that C# generic and non generic APIs are completely different, and are not inter operable.
- kjeetgill 8y agoYea from the wording I think they mean you can either break backwards compatibility or "fork the world". In Java's case, old bytecode using Map would correctly work with change to Map <K, V>. C# introduced a new set of collections I believe.
- mohaine 8y agoNot sure on the first part but the second part is true. When there is a C# class that both has and doesn't have type information, they are actually completely different classes that just happen to share a base name. This really made it painful when they first added generics. If you used generic containers but wanted to call into a library that predated it you hand to transform your data in/out of all the calls to the library. In Java land you didn't have to do anything other then a blind cast on the out side which doesn't even have a runtime penalty. Long term the C# way was probably better but then they didn't have nearly as robust of a library ecosystem when they added generics.
- monocasa 8y agoYeah, IIRC they added the "constrainted." instruction prefix. It's a prefix to the callvirt instruction that constrains the the underlying call to the specified type, allowing runtime generic type decisions/exceptions rather than only compile time decisions like Java.
- bunderbunder 8y agoTo the first - yes, the version of .NET that introduced generics broke bytecode backward compatibility. Stuff compiled for .NET 1.0 will not run on the CLR for 2.0 and later. In practice, this wasn't a big deal; maintainers just shipped two different versions of their packages, and you'd download the one you wanted. To the 2nd - The concrete types are separate, but they fit into a common interface hierarchy, so, at a source code level, they're plenty interoperable. For example, the (non-generic) IEnumerable interface has a Cast<T>() extension method that will convert it to a (generic) IEnumerable<T>. IEnumerable<T>, for its part, simply inherits IEnumerable. In general, I actually like using those conversion methods for downcasting, because it gives a clearer indication of when I'm in a danger zone - for example, converting a non-generic list to a generic list might fail if the non-generic list contains a mix of different types of object. Java's situation feels less predictable to me, as this article illustrates fairly well. A few keystrokes for the sake of safer code is a dandy tradeoff in my book.
- teh_klev 8y ago> Stuff compiled for .NET 1.0 will not run on the CLR for 2.0 and later That's not true. (Mass shared hoster kinda guy here) when we pushed a bunch of customers code compiled for .NET 1.0/1.1 over to hosting environments with only CLR 2 available, their code ran just fine. These were .NET 1.0/1.1 assemblies. There are some edge cases where stuff could break, say when doing reflection or emitting your own IL, but that for most LOB web hosted apps was pretty rare. I did in fact marvel how backwards compatible CLR 2 was when it first shipped.
- pjmlp 8y agoThe missing part every time this story gets told, is that Microsoft already had a generics implementation ongoing being lead by Don Syme of F# fame. They just decided to release 1.0 without generics, instead of waiting for it to be 100% ready. As Don clearly describes on his blog.
- monocasa 8y agoSure, totally. In fact, I'm a big fan of how Microsoft does versioning, supporting previous versions, but releasing major versions with backwards incompatible changes. You see this in DirectX, and IMO, that's why we're talking about DirectX and Vulkan, rather than AZDO OpenGL these days. It really gives you an opportunity to clean up the cruft.
- pjmlp 8y agoYeah, although I think they missed an opportunity to move beyond "only C goes" mindset at Khronos.
- majewsky 8y agoIs there any language besides C that's not a complete pita to generate FFI bindings for?
- bunderbunder 8y agofortran?
- pjmlp 8y agoThere are many ways to define APIs, we don't need to struggle ourselves only to C. Metal uses Objective-C/Swift, with shaders in C++14. The Objective-C runtime can be used as FFI. DirectX, uses COM with HLSL (a C++ subset). Likewise any COM or .NET (via RCW) aware language can talk to it. One of the reasons OpenCL lost to CUDA was being stuck with C, while CUDA offered C, C++, Fortran and any additional language that could target PTX. Which was what eventually made them come up with SPIR and later SPIR-V. NVN and LibCGNM are also based on C++ and C++ inspired shader languages. All modern OO, with nice SDKs that handle font, texture, materials, maths, GPGPU debugging.
- pron 8y ago> added it without many issues Except for the fact that all CLR languages must adopt the same variance model, enforced by the runtime (or choose not to interoperate well).
- monocasa 8y agoSure, but in context Go doesn't have that issue. Go's compiler only supports Go in a more or less statically linked binary.
- pron 8y agoGo doesn't have this issue because there aren't other languages running on that runtime. If there were (and you wanted an interop of those languages with Go so that you could use Go libraries), then you'd have this issue regardless of the fact that the runtime is linked with the image.
- deleted 8y ago[deleted]
- alexeiz 8y ago> Take a look at C# which added it without many issues I wouldn't say "without many issues." It took a major surgery in the CLR codebase to add generics. The difference between CLR 1.1 without generics and CLR 2.0 with generics was huge. No area of code was untouched.
- mnd999 8y agoExcept they didn’t - they introduced annotations at the same time and bumped the class file version for that.
- jeremyjh 8y agoChanging the class file doesn't necessarily break backwards compatibility. Are you saying a 1.4 JVM couldn't run 1.3 bytecode?
- mcculley 8y agoGo doesn’t have an ABI? Does every library ship only as source code? I didn’t know that. I was thinking of more fundamental changes to Java idiom and the standard library had generics been part of the design (e.g., how arrays work).
- kjeetgill 8y agoInteresting. How do you think it would be different? I ask because the go-to is usually genetics erasure. Do you think more could or would have been different? For what it's worth, Brian Goetz doesn't see type-erasure as a such a failure and defends the design choice[0]. [0]:https://m.youtube.com/watch?v=TkpcuL1t1lY https://m.youtube.com/watch?v=TkpcuL1t1lY
- hota_mazi 8y agoActually, most people who actually understand in depth the subject and the trade offs of erasure versus reification squarely side on the erasure side. Erasure is the safest way to implement generics for a long list of reasons. Reification comes with a lot of downsides, which is why most languages that support parametric polymorphism use erasure.
- kjeetgill 8y agoInteresting, do you have any resources? What people? Aside from the video I posted from Brian Goetz most of what I've read is just comments about how much people like reification in C# and how annoying those edge cases in Java are (i.e. stories from practitioners not language designers.) I'd love to read more.
- hota_mazi 8y agoThere's plenty of material out there but I'll summarize: 1. Erasure keeps you honest and prevents you from second guessing the compiler by limiting the introspection you can perform on your code. 2. Reification puts up a very high barrier to interop. Erasure systems are much more welcoming to implementing multiple languages and multiple type systems on top of them. For example, the scala.net project was abandoned because it was impossible to represent Scala's type system on .net's reified platform. 3. Reification imposes overhead because of the multitude of extra runtime checks that need to be performed. 4. Most the benefits you get from reification can be emulated on erased systems (some are admittedly more hackish on an erased runtime, but they should be rare).
- MaxBarraclough 8y ago> If Java had generics in the beginning, it would look a lot different and the type system would be more powerful and safe. And conceivably a good deal faster, too. My understanding is that the JVM is required to constantly perform runtime type checks.
- monocasa 8y agoYeah, it either has to cast from Object all the time, or spend JIT compiler time and memory figuring out if it can elide those checks (probably with exception code for when it guesses wrong).
- leshow 8y agoand pointer chasing too right? unless the runtime can optimize, every generic sits behind a pointer.
- icedchai 8y agoGood deal faster? I seriously doubt it. My understanding are generics are purely a compile time feature and does not effect run time. I remember Java before generics: tons of casting from Object. The compiler is doing the same thing.
- mcculley 8y agoThose casts are still there. The compiler puts them in and the JVM has to execute them.
- piinbinary 8y agoSince there is no subclassing [0] in Go, many of those cases are not expressible in Go. [0] Interfaces can contain other interfaces, but I think that is still different because you can't do var a []subinterface = []superinterface{...}
- tabtab 8y agoJust use dynamic or better yet type-tag-free languages. Types are hard to get right and hard to reason about. Sure, there are some Sheldons out there who master it even under pasta applications, but most of us are not Sheldons. C# also bleeped up types by adding nullable types, making reflection into a scavenger hunt. For dynamic or tag-free languages, type indicators could be used to parse-check scalar (base) values to see if they are interpretable as the intended type: function foo(int a, date b) {...} This would be equivalent to: function foo(a, b) { if (! parsableAsInt(a)) throwTypeError(...); if (! parsableAsDate(b)) throwTypeError(...); } And don't overload operators, such as how some languages use "+" to mean both arithmetic addition and string concatenation. Use a different symbol for concatenation like PHP does.
- recursive 8y ago> C# also bleeped up types by adding nullable types, making reflection into a scavenger hunt. Having used reflection in C# with both nullable types and non-, I'm curious what you mean by "scavenger hunt".
- deleted 8y ago[deleted]
- dep_b 8y agoString concatenation got a lot nicer in C# lately but the old method is still there
- erik_seaberg 8y agoPHP only got this halfway right because it borrowed "." from Perl. It should have also borrowed "eq" instead of making "==" unusable.
- twblalock 8y agoI prefer my type errors at compile time. You have to reason about types in any language because passing the wrong type to a function can result in errors. You have to do all of that mental work by yourself in a dynamic language because there isn't a system to do it for you.
- sqrt17 8y agoGo's type system with limited polymorphism looks to me like someone took the (horrible) collections from Java 1.2 and applied a fix that solves 85% of the problem. Compared to the 95% solution we get with Java, it's not as refined but also plenty good enough in most cases where concrete code deals with concrete problems (i.e. not framework-level code where you'd curse Java for lacking Scala's types-and-implicit-driven compile time contorsions).
- microtherion 8y agoMy impression (having not tried this part of the language much) is that Swift underwent similar growing pains with their generics (but with the benefit of being willing to break backward compatibility). After 40 years or so of generics, it seems language designers STILL think "all these languages before me are overcomplicating the matter, I bet I can do this in a much simpler way".
- slavapestov 8y agoThe implementation of generics has undergone major changes and new features were added, but the conceptual model is pretty much what it was in Swift 1. This was one part of the language that they almost got right from the start.
- kelnos 8y agoI was reading something a while ago (can't find it now, unfortunately) that suggested that the use of type erasure for generics actually opens up the JVM to be able to do some useful things that (for example) the CLR can't do. One thing I recall was that the Scala developers gave up on their CLR/.NET backend. I also read, more generally, that adding reified generics to the JVM might make it hard or impossible to implement some dynamic languages on top of the JVM. So yes, erasure has its drawbacks, but it's not all bad.
- chrisseaton 8y agoYes I've heard this from multiple people targeting new languages to the JVM. Maybe it's why the JVM has a larger ecosystem of new languages?
- hyperpape 8y agoOne source: https://olabini.com/blog/2010/07/questioning-the-reality-of-generics/ https://olabini.com/blog/2010/07/questioning-the-reality-of-...