• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Rob Spoor
  • Tim Cooke
  • Junilu Lacar
Sheriffs:
  • Henry Wong
  • Liutauras Vilda
  • Jeanne Boyarsky
Saloon Keepers:
  • Jesse Silverman
  • Tim Holloway
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Mikalai Zaikin
  • Piet Souris

Invariant generics, covariant arrays, making me wonder if type safety is worth it at all

 
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Decades ago, I recall a quote regarding statically typed languages (such as C/C++/Java/etc.):

"Statically typed languages serve to solve the problem caused by statically typed languages to begin with!"

(and this was obviously a plug for dynamically typed languages). I am fully in this camp, and I think that our faith in the necessity of statically typed languages is misplaced. We can discuss that some other time in some other forum.

But now as I see the in-depth problems with Generics, I am again wondering if we're once again barking up the wrong tree.

I cannot, for instance, do this in java:

The problem with the above is rooted in the oddity that arrays, while theoretically a type safe object, are covariant:

but generics (without getting syntactically gross with the "? extends E" idiom) are invariant:

So how exactly are generics a win at all? Well the first and most common response is something akin to "Well, because we can move unsafe typecasts from being a runtime consideration to a compile time error." But is that really that much of a worry? Most of the time that we are using generics we are specifying a compound datatype that is unified throughout a collection:

(or using the <> shortcut):

...so we're going to fail very quickly with the first test. Basically, I am less and less seeing the pitfalls in this pre-generics method:

and further am actually starting to wish that the language allowed implict universal casting (let the errors fall where they may):

Now I understand I'll likely be in the minority here (strong and generic typing being a good thing is a very commonly held belief for some reason), but the language seems to becoming uglier by the year (not to mention java8 is part functional AND object oriented). There's a point where type protection no longer adds protection, but just produces clumsy looking code, and I think we may have long since reached it.
 
Marshal
Posts: 73979
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Welcome to the Ranch

I think a large part of the problem with Java® generics is that they were not introduced on day 1. Maybe they ran out of time or money or both. So as not to lose backwards compatibility generics is implemented via erasure. Maybe if they had intriduced generics at the beginning and used reification you wouldn't now be seeing that problem.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Welcome to the Ranch

I think a large part of the problem with Java® generics is that they were not introduced on day 1. Maybe they ran out of time or money or both. So as not to lose backwards compatibility generics is implemented via erasure. Maybe if they had intriduced generics at the beginning and used reification you wouldn't now be seeing that problem.



It's possible. But I'm trying to speak past the clumsy way of how it's implemented and actually address whether or not they're worth the effort even if they were cleanly implemented.

For instance, how much is truly gained by having or? I'm just not sure that the rare runtime casting error is worth pushing the entire check into compile time. I coded pre-generics java for years (since 1996....when things were truly rotten) using the following idiom: without any long term or difficult to find bugs.
 
Campbell Ritchie
Marshal
Posts: 73979
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I would have thought any bugs with casting would be really easy to find I suspect a lot of people complained that other languages had generics, including C++ and C#, and wanted the check pushed earlier to compile time.
 
Java Cowboy
Posts: 16084
88
Android Scala IntelliJ IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Some features in Java are implemented in a less than ideal way, and that mainly has to do with backward compatibility.

Early on someone decided that arrays should be covariant, which in hindsight is perhaps an unfortunate design decision, which made combining arrays and generics impossible in certain ways. At the time generics were added (in Java 5) it was too late to make arrays invariant, because of backward compatibility.

Oracle (and previously Sun) has always regarded backward compatibility of newer Java versions with older versions as essential, that's why we still have raw types and deprecated stuff in the standard library that is never going to be removed.

I am strongly in favour of static typing. The earlier you catch errors in a program, the cheaper these errors are to fix. It is always better to have an error show up at compile time than to have it show up much later, at runtime. At runtime, you won't notice an error until the code with the error is executed. Especially if the error first occurs when the software is already deployed in production, it will be much, much more expensive to fix than when it would have been caught at compile time.

Type casts deliberately circumvent the compiler's type checking, moving a potential error from compile time to runtime (where a ClassCastException will occur if the type is wrong). Therefore type casts should be avoided as much as possible. Generics help you avoid type casts, enabling the compiler to do type checking at compile time, instead of letting the JVM do it at runtime. Besides that, type casts have a small performance cost (because a type check has to be done at runtime anytime the code is run).

Generics make the language safer and better, despite the clumsiness, which is mainly caused by the need to keep everything backward compatible.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesper de Jong wrote:I am strongly in favour of static typing. The earlier you catch errors in a program, the cheaper these errors are to fix. It is always better to have an error show up at compile time than to have it show up much later, at runtime. At runtime, you won't notice an error until the code with the error is executed. Especially if the error first occurs when the software is already deployed in production, it will be much, much more expensive to fix than when it would have been caught at compile time.



That's the classic response, yes. But I would argue that it ignores the fact that actually using statically typed languages has its own cost: it increases development time. To many of us, it's much better to simply use and let the compiler (or runtime) figure out how to declare things.

Whenever I've used a dynamically typed language, the number of times I've been significantly hung up by a type mismatch has been precisely zero. It just doesn't happen.

Similarly, in pre-generics java, the number of times I've been held up by a runtime cast exception that was significantly hard to find was also zero. That didn't happen in painful ways----it just didn't.

And meanwhile, we're addicted to statically typed languages in the hopes of staving off problems that don't even really exist.

The vast majority of my development has been with statically typed languages, and Java among my most favorite. But static typing has its cost that we're just too quick to overlook.

 
Sheriff
Posts: 26770
82
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here's another take on the question which I haven't seen elsewhere.

If you hang around this forum for a while and watch what beginners ask, you will see people who want to make a List consisting of a variety of different kinds of object. Usually they are fumbling around trying to figure out how to cast these objects when they go to get them back out of the list.

And usually they have made a design error by trying to produce a heterogeneous list like that. So what I'm saying is this: the presence of generics in the language implies that a List should (almost) always be a homogeneous list, a List<Something> in fact. Which means that not only is the presence of generics preventing run-time errors by presenting them as compile-time errors, it's also preventing design errors by constraining what code you can write.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Paul Clapham wrote:Here's another take on the question which I haven't seen elsewhere.

If you hang around this forum for a while and watch what beginners ask, you will see people who want to make a List consisting of a variety of different kinds of object. Usually they are fumbling around trying to figure out how to cast these objects when they go to get them back out of the list.

And usually they have made a design error by trying to produce a heterogeneous list like that. So what I'm saying is this: the presence of generics in the language implies that a List should (almost) always be a homogeneous list, a List<Something> in fact. Which means that not only is the presence of generics preventing run-time errors by presenting them as compile-time errors, it's also preventing design errors by constraining what code you can write.



Interesting point. But I'm not completely sure of it. Consider the following java pseudo-code:


They still are able to make that same bad design:



 
Paul Clapham
Sheriff
Posts: 26770
82
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sure, I'm not saying it prevents that design (which isn't all that bad because it isn't that heterogeneous), only that it dissuades the user from charging ahead with it. At least it makes them think about what they are doing. And there should be a rule of thumb which says that if you're casting (and you already used Generics) then that code should be reviewed to make sure that there isn't some better design.



 
Jesper de Jong
Java Cowboy
Posts: 16084
88
Android Scala IntelliJ IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Thomas Gard wrote:But I would argue that it ignores the fact that actually using statically typed languages has its own cost: it increases development time. To many of us, it's much better to simply use and let the compiler (or runtime) figure out how to declare things.


I disagree that it increases development time significantly; on the contrary, it helps to make code easier to understand, which saves you time. Besides providing more type-safety, generics help as documentation, so that you understand quickly what exactly a method returns, for example. Suppose that you have some method that returns a List in some unfamiliar project. How do you know what the type of the elements in the List is? Without generics it's hard to find out. With generics, you can see it immediately.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesper de Jong wrote:

Thomas Gard wrote:But I would argue that it ignores the fact that actually using statically typed languages has its own cost: it increases development time. To many of us, it's much better to simply use and let the compiler (or runtime) figure out how to declare things.


I disagree that it increases development time significantly; on the contrary, it helps to make code easier to understand, which saves you time. Besides providing more type-safety, generics help as documentation, so that you understand quickly what exactly a method returns, for example. Suppose that you have some method that returns a List in some unfamiliar project. How do you know what the type of the elements in the List is? Without generics it's hard to find out. With generics, you can see it immediately.



Have you spent considerable amounts of time in dynamically typed languages? It's a much smoother experience, even though I'm most familiar with the rigidity of statically typed languages.

There's an additional quirk also that in fully dynamic languages, you don't need inheritance to experience polymorphism. In fact, there was a famous quote once upon a time: "polymorphism is more important than inheritance".

But directly to your point: In that example you gave, you cannot (or SHOULD not) use the return values of a method until you know what it's supposed to be. The actual compound datatype is the very least of it. For instance, if it's returning a List<String> then, you need to know that anyway before using. No one should ever just call a method without knowing what it's supposed to do in the first place. That's the very very first entrance fee to coding....you don't get around that with statically defined compound types.

Also, I used to be a fan of statically typed languages, right up until I realized that there is very little win. It doesn't really make code easier to understand. It's only something you may be used to. Code devoid of statically defined types is the cleanest looking stuff around.

I was afraid that this was going to fork off into the age old "dynamic vs. static language" debate that historically has absolutely no resolution. But it seems unavoidable because it's the basis for me questioning the validity of these generics (and in particular, this clumsy implementation Java was forced to implement). And I think it's time we more regularly question certain hallowed grounds.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Paul Clapham wrote:Sure, I'm not saying it prevents that design (which isn't all that bad because it isn't that heterogeneous),



Well, generics or no generics, I'd argue that it is bad because it still involves pulling different objects off of the list. They're only nominally unified by Fruit. If the behavior itself isn't accessible by Fruit, even as a simple java interface, then the design is bad. In otherwords, ideally there should be no reason to differentiate an Apple from an Orange if the design were clean. Though of course, not everything goes to plan.

This, however, is different from the unprotected casting idiom that would be required without generics. I don't have an issue with that so long as the collection is of homogenous behavior.

 
Rancher
Posts: 4801
50
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Thomas Gard wrote:

Have you spent considerable amounts of time in dynamically typed languages? It's a much smoother experience, even though I'm most familiar with the rigidity of statically typed languages.



Does Javascript count?
Because that is one pain in the posterior with some of the "interesting" code I've had to work with, all done based on the freedom the language gives.

You argue that you should know what is passed back, however it is anything but clear in your average script.
At least with Java I know (outside of a few frameworks) what I need to be dealing with just from looking at the signature. And you can usually bury those framework interactions (eg some Hibernate stuff) into the smallest scope.
 
Thomas Gard
Ranch Hand
Posts: 72
2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Dave Tolls wrote:

Does Javascript count?
Because that is one pain in the posterior with some of the "interesting" code I've had to work with, all done based on the freedom the language gives.

You argue that you should know what is passed back, however it is anything but clear in your average script.
At least with Java I know (outside of a few frameworks) what I need to be dealing with just from looking at the signature. And you can usually bury those framework interactions (eg some Hibernate stuff) into the smallest scope.




No, I'm saying that you don't know what a routine is doing simply by knowing the datatypes returned. You just don't. And it doesn't even help. And once you *do* know what the routine is doing, the last thing in the world that matters is the type.

But I'm not intending on winning anyone over to the dynamic types camp. In fact, it took me quite a while to let go of it myself, so I certainly am not expecting anything there. Nor would I even attempt anything to that extent in a Java forum. So we can shake hands and leave that part of the discussion behind.

What I was hoping to focus on was whether or not generics actually end up doing more harm than good. I am comfortable using generics....I just don't like that arrays are covariant, and generic types are invariant, and further that you cannot mix the two. It's just not intuitive, and I don't see the win in all of this.

In fact, I'll bet if you ask around you'll find that most folks who are "fans" of generics don't even know what the terms covariant and invariant mean, let alone that you can't declare an array of a generic type.
 
Paul Clapham
Sheriff
Posts: 26770
82
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Thomas Gard wrote:I just don't like that arrays are covariant, and generic types are invariant, and further that you cannot mix the two.



That might bother me in theory, but in practice I've found that I almost never use arrays any more. For me they are a kind of legacy feature which I'll use if I have to use them to interface with someone else's API.
 
Dave Tolls
Rancher
Posts: 4801
50
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Thomas Gard wrote:
No, I'm saying that you don't know what a routine is doing simply by knowing the datatypes returned. You just don't. And it doesn't even help. And once you *do* know what the routine is doing, the last thing in the world that matters is the type.



Well, you know what it's contracted to do, and the return type is a part of that. If someone is breaking the contract then that's a whole other problem.

Thomas Gard wrote:
But I'm not intending on winning anyone over to the dynamic types camp. In fact, it took me quite a while to let go of it myself, so I certainly am not expecting anything there. Nor would I even attempt anything to that extent in a Java forum. So we can shake hands and leave that part of the discussion behind.



Fair enough.

Thomas Gard wrote:
What I was hoping to focus on was whether or not generics actually end up doing more harm than good. I am comfortable using generics....I just don't like that arrays are covariant, and generic types are invariant, and further that you cannot mix the two. It's just not intuitive, and I don't see the win in all of this.

In fact, I'll bet if you ask around you'll find that most folks who are "fans" of generics don't even know what the terms covariant and invariant mean, let alone that you can't declare an array of a generic type.



As Paul says, though, arrays are not common sight in Java code, so the covariant nature hardly ever comes up. I can't remember the last time I had to use them outside of possibly a byte[] buffer.
 
Rancher
Posts: 1043
6
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Thomas Gard wrote:

Have you spent considerable amounts of time in dynamically typed languages?




There is a legend that Dennis Ritchie, inventor of C, once responded to demands for features resembling those of what at the time was a much more popular language by observing “If you want PL/I, you know where to find it.

If you want X, you know where to find it.
 
Campbell Ritchie
Marshal
Posts: 73979
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Have you ever read Bertrand Meyer's opinion of C? In Object‑Oriented Software Construction 2/e 1997 (Prentice‑Hall)?

Bertrand Meyer's book, page 1106-7 wrote:Born in a log cabinet, C quickly rose to prominence …
Politically, C ended the fossilized situation which prevailed in the programming language world until about 1980.
… Fortran … Cobol … PL/I …
C [… made] it acceptable to think of the programming language as something you choose from a reasonable broad and evolving catalog.

Meyer is very good at identifying the good and bad features of languages and writing them down succinctly.
 
You showed up just in time for the waffles! And this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
reply
    Bookmark Topic Watch Topic
  • New Topic