Hacker News new | ask | show | jobs
by jstimpfle 2928 days ago
Nice work. But, serious question - When going with parameterized types I know from Haskell how you always end up needing one more language extension and always end up banging your head against the wall that separates types and values a little more. At least if you're not a math genius, but probably even then.

And from Java I know that there's a pretty trivial example that showcases how its Generics implementation is unsound when mixed with inheritance.

And I know that the Go maintainers have been hesitant for a long time because they didn't know a good version of Generics to add.

So, is there any version that just works, and never leads the user down any rabbit holes? And that doesn't lead to ever increasing type boilerplate?

Because I've been super happy ever since I decided that worrying about occasional usage of void pointers in C is just not worth my time. And where configurability is really needed, function pointers are totally fine - I don't think there is any need for static polymorphic dispatch (function pointers are probably even preferable, to avoid bloated machine code).

3 comments

Haskell is a proving ground, so it's picked up a number of ideas that never quite panned out.

I think you can identify a core set of features that are pretty reasonable. I think that'd be type classes, constraints, and functional dependencies.

If they made a handful of extensions standard (GADTs, etc.) it would mostly Do What You Want without a lot of prodding.

> And from Java I know that there's a pretty trivial example that showcases how its Generics implementation is unsound when mixed with inheritance.

I'd be curious to see that. There's a well known limitation that mutable containers (and they're all mutable in Java) need to be invariant, but that doesn't make them unsound.

The type system was also already unsound due to covariant arrays and nulls being a member of all classes, but if you don't break those rules or disable checks, Java generics work as far as I can tell. By "work", I mean I've yet to get a ClassCastException in a fair amount of work with some gnarly Java generics.

And, really, 99% of the boilerplate in Java's typing is that you can't declare aliases for types; that seems to be more due to engrained hostility to syntactic sugar than any technical difficulty.

> So, is there any version that just works, and never leads the user down any rabbit holes?

Most of the "gradual typing" projects for languages like Javascript, Python, Ruby all seem to accomplish what you're looking for, by virtue of the fact that you can just ignore it when you don't want it.

> There's a well known limitation that mutable containers (and they're all mutable in Java) need to be invariant, but that doesn't make them unsound.

I see. Yeah I don't know precisely what these terms mean. If the creators of Generics mistakenly made them covariant, that only goes to show that maybe it's a little too complicated. IMHO.

To be more precise, what we want to do might be too complicated for practical (i.e. relatively simple) type systems to describe. So, why bother at all? Better learn how to structure programs simple enough to make them obviously correct (i.e. mistakes will be obvious and can be easily fixed). Instead of catering to the needs of impractical type systems. I think that's why C is still so popular: It removes most of the boilerplate (i.e. strides for array indexing, arithmetic operators, structs, other ABI things) but gets out of the users way if s/he needs to disregard these constraints for a while.

Even in C, there is a similar problem with const compatibility of pointers of more than 1 level of indirection. Example taken from [1]

    const int **pp2;
    int *p1;
    const int n = 13;
 
    pp2 = &p1; /* not allowed, but suppose it were */
    *pp2 = &n; /** valid, both const, but sets p1 to point at n */
    *p1 = 10;  /* valid, but changes const n */
[1] https://www.sanfoundry.com/c-tutorials-concept-pointer-compa...
CLU was the first language to implement generics in 1975.

There are many levels of generic capabilities, across multiple languages in about 45 years of research, we don't need the full shop, CLU generics would already be quite usefull versus interface{} everywhere.

With Java I assume you mean that you can create two arrays that are subtypes of each other if their contents are subtypes? i.e., `int[] < double[]`, or `Subclass[] < Superclass[]`?

If so, this is known wrong in the programming language theory community. Java just gets this wrong; if you remove it, generics in Java work correctly, I think.