| Practically speaking, Haskell's type system gives you a lot of code for free. Stuff like "toString", "equals" and inequalities that you'd usually implement manually in something like Java are done for you automatically by the compiler, with a one-line directive. That system is extensible as well, so for example you can automatically get Serde code for stuff like JSON, Avro, Protobuf etc with a one-liner. On a more abstract level, there's a lot of stuff you can express in Haskell that's difficult or impossible on a technical level in Java. For example: - Functions which are polymorphic in their return type, so that what they do is determined by what type the caller wants them to return. - 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". - 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. |
> - 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.