You need some better examples (your examples all appear expressible in Java):
> - Functions which are polymorphic in their return type, so that what they do is determined by what type the caller wants them to return
You'll have to be more specific here - polymophism in the return type is clearly trivial w/o specific constraints, e.g.
<T> T identity(T x) { return x; }
> - Function constraints - for example try to express "A function which takes two polymorphic arguments, which must both be of the same type, and which must be orderable (i.e have <, ==, >, etc defined for them), and returns the same type" in Java. In Haskell, that's just "f :: Ord a => a -> a -> a".
<T extends Comparable<T>> T f(T a1, T a2)
> - Higher kinded types. These let you have (loosely speaking) polymorphic containers. For example, instead of List<A> and Set<A>, in Haskell you'd have something like Traversable t => t a, where Traversable is a particular interface you want your "container" to implement.
Iterable<T>
If you're arguing that the Java version requires the types to declare that they implement Iterable, Comparable, etc, I think that's more a philosophical distinction about programmer responsibilities rather than a technical type system difference.
Haskell does have a fancier type system than Java, so you can actually present some interesting differences. However, my belief and observation is that for many smart programmers, Java's type system is already "too fancy", i.e., too hard for many people to use effectively. So I'm somewhat skeptical of the extra value brought by Haskell's type system.
I think one distinction is that typeclasses let you add some new 'interface' like Fooable and then implement them for standard types like String and Int as well as allow the user of the library to implement a version as well. With generic programming like Shapeless in Scala one could also recursively derive a Json decoder based on having Decode defined for all the components types of some nested arbitrary data object.
A lot of this stuff is possible in other languages for varying definitions of 'possible' which usually comes down to how much code you have to write to do this, how flexible it is with code you haven't explicitly written (i.e. standard prelude types or what have you) and how much the ecosystems that exist in these languages are oriented in a manner which takes advantage of these features.
Consider nullability in Java and how pervasive it is. Java has tons of great software but the type system is lacking based on how many functions could return null but don't document it, could throw an exception but don't document it, etc... This is predominantly because actually handling nullability on the type level, or error handling on the type level is not straightforward in Java.
> You'll have to be more specific here - polymophism in the return type is clearly trivial w/o specific constraints, e.g. <T> T identity(T x) { return x; }
Consider a function "decode :: Read a => String -> a". What this returns (and what it does) is dependent on the type that the caller expects.
> <T extends Comparable<T>> T f(T a1, T a2)
Java may have improved this somewhat since I last used it, but the general complaint from Haskellers on this is how difficult it is to say that types must be equal. Consider the fact that java `.equals` is implemented in terms of `Object`, so there's no requirement that the argument be of the same type (or even of a comparable type) to the originating object. Contrast to Haskell's `==`, which can only be called with the same type on both sides. (There is no concept of referential equality in Haskell, so no equivalent to Java's `==`).
Also, I think you'd struggle with that extends trick once you started getting more complicated constraints. e.g, try something like: "T is traversable, A is orderable and serializable to JSON, and T<A> is a monoid".
> - Functions which are polymorphic in their return type, so that what they do is determined by what type the caller wants them to return
You'll have to be more specific here - polymophism in the return type is clearly trivial w/o specific constraints, e.g. <T> T identity(T x) { return x; }
> - Function constraints - for example try to express "A function which takes two polymorphic arguments, which must both be of the same type, and which must be orderable (i.e have <, ==, >, etc defined for them), and returns the same type" in Java. In Haskell, that's just "f :: Ord a => a -> a -> a".
<T extends Comparable<T>> T f(T a1, T a2)
> - Higher kinded types. These let you have (loosely speaking) polymorphic containers. For example, instead of List<A> and Set<A>, in Haskell you'd have something like Traversable t => t a, where Traversable is a particular interface you want your "container" to implement.
Iterable<T>
If you're arguing that the Java version requires the types to declare that they implement Iterable, Comparable, etc, I think that's more a philosophical distinction about programmer responsibilities rather than a technical type system difference.
Haskell does have a fancier type system than Java, so you can actually present some interesting differences. However, my belief and observation is that for many smart programmers, Java's type system is already "too fancy", i.e., too hard for many people to use effectively. So I'm somewhat skeptical of the extra value brought by Haskell's type system.