3 ms·
Haskell's type system is _worlds_ better than Java's, and it's absolutely essential to the maintenance benefits that Haskell gives you over other languages.
by thedataangel 8y ago
Haskell's type system is _worlds_ better than Java's, and it's absolutely essential to the maintenance benefits that Haskell gives you over other languages.
- crehn 8y agoJust curious, but care to give a practical example of how it's worlds better?
- thesz 8y agoType system can prevent classes of mistakes. One such example in my experience is modeling (and designing) hardware. Strict checking of lengths of bit vectors (just bit vectors!) will make mistakes like "incorrect interpretation of memory address" much harder to be made. Add another one: the "ready" bit for some bus values can be described as Maybe type. Pattern matching on value of Maybe type makes impossible to access bus values when "ready" bit is down (Nothing case). You get rid of another class of mistakes. For this to work in more or less usable way, you have to be able to specify in type level equalities like "concatBitVectors :: (rlen ~ Add alen blen) => BitVector alen -> BitVector blen -> BitVector rlen". This is just not possible in Java. It is possible in C++, but C++ lacks algebraic types like Maybe and more. It is possible in Scala, but Scala has its own pecularities (modifiable variables are absent in hardware).
- thedataangel 8y agoPractically 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.
- davidgay 8y agoYou 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.
- thedataangel 8y ago> 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". > Higher kinded types This probably gives a better explanation than I can be bothered writing: https://stackoverflow.com/questions/35951818/why-can-the-monad-interface-not-be-declared-in-java https://stackoverflow.com/questions/35951818/why-can-the-mon... The TLDR is that there are some concepts involving higher-kinded types (such as Monads) which are simply inexpressible in Java's type system.
- marcosdumay 8y agoIt is not comparable. Haskell's type system is a qualitatively different thing from Java's, with different use cases (it can be used the same way as Java's, but people just don't). Java's types don't help you organize your code, don't help you make it more concise, and enable only the shallowest kind of code generalization. They are not expressive enough to prove correctness, they mostly do not help you hiding information away from the developers focus, and they mostly do not allow you to restrict code into their responsibility.
- willtim 8y agoSum types, not having null in every type, multiple dispatch (useful for numbers) and higher-kinded polymorphism (e.g. X can also be a type parameter in X[T]) are at least four very useful features that Haskell has and Java doesn't. Haskell also has excellent type inference that makes it as succinct as Python despite the static types. In practice, despite the features Java can support, it's typically too much work to define a new type for safety reasons. Strings are used everywhere, instead of more domain specific types. Even the designers made compareTo return an Int!
- btschaegg 8y agoSo much this. I'm not actually that much of a fan of Haskell (might be due to a bad introduction to it), but dabbling in F#, I'm constantly amazed by how much more robust (and self-describing) you can design your APIs while still being way more concise than when using Java or C#. Since Haskell's type system is still more powerful than what is achievable with the .NET CLR, I'd assume this effect to be even stronger there. It's really amazing how far sane language defaults can get you. In C#, I encounter badly designed types all the time. The main reason for this is not that it is impossible to design them right, but that the language makes it so tedious. I'm also really longing for the new features in C# 8, in the hope that they manage to remedy some of that (record types, pattern matching and non-nullable references sound like a good start).
- platz 8y agoAs a c# person, Sadly the pattern matching will not do exhaustiveness checking which makes adding new data to cases much more error prone than without it, so there is less incentive to move away from inheritance
- akra 8y agoI suspect there is a point of diminishing returns with this though; the extra complexity in the type system gets you less and less benefit but at a greater cost to code complexity/comprehension. It could explain why the mixed Scala/F# paradigm has more industry usage than Haskell right now. i.e. I am trading a type system that has a little more expressiveness for a large amount of libraries, ecosystem, rock-solid VM, testing, corporate buy-in, etc. In a .NET shop for example its much easier to just drop an F# DLL into your codebase (in VS create a new F# project in a few clicks); there's no need to change anything else (e.g. build machines, build tooling, testing tools) since it's all IL anyway; same goes with Scala and Java. My experience in coding functionally has shown that mutation (especially if localized and doesn't leak outside of a function) can really help functional code become a lot faster. From what you describe C# is slowly becoming an F# clone; which may be a good thing or not. Still think that if you need these features you should move to Scala/F#/Ocaml/Haskell though since I suspect these features will be added to C# in a clumsy/compromise way given its roots (e.g Scala and F# already have exhaustive pattern matching, async streams/iterators, better type inference, etc.).
- joel_ms 8y agoI feel like it's hard to give a concrete practical code example, but maybe I can describe my practical experience. The main difference I've experienced is that while java's type system forces you to be explicit about your types in a lot of places – like implementing interfaces, assigning types to variable declarations, and just in general the fact that "type inference" is limited to expressions – you get very poor checking of those types, since a lot of their complexity is hidden in the object hierarchy. This in turn, is due to java's orgins as a object oriented language relying on subtyping and inheritance for polymorphism (generics improved somewhat on this.) The result is that a lot of the complexity that haskell gets accused of, ends up in complex design patterns and/or object oriented design principles in java. This has gotten better as java has loosened some of its initial restrictions (e.g. java 8 allowing first-class functions.) Haskell on the other hand was designed with parametric polymorphism (generics in java terms) in mind and as a functional programming language with first-class functions and no object orientation, allowing it to reap more of the benefits from the more theoretical research into type theory and category theory (yes, this gets us into monads, but I don't think they are nearly as mysterious as the internet makes them out to be.) In my opinion this has made the abstractions in haskell a lot more sturdy and more importantly statically checkable, compared to the object-oriented design principles underpinning a lot of design patterns in java. Haskell also has global type inference, which means you can avoid explicit types in many cases, especially when prototyping small, pure functions. This benefit is somewhat lessened by the fact that haskell's error messages can get very complex, but I subjectively believe that this is due to the fact the type system is checking a lot more and a lot more of what gets checked doesn't have to be given an explicit type by the programmer. Given this I pretty clearly prefer haskell, but I've spent most of my career writing java and php. In fact, most of my criticism of java drove me to dynamic languages at first (although I would have preferred python over php.) What got me interested in haskell was the fact that I felt that there should be a way to have the freedom and speed of developing in a dynamic languages, while having the same (or better) guarantees of a statically typed language. This what led me to read about type inference. Now, the complexeties of haskell (or even worse, haskell with ghc extensions) hardly makes my dream a reality, but I still feel like I write a lot less types while having the compiler do a lot more work for me. What has made my dream more of a reality is actually elm[0], which has similar theoretical foundations as haskell, but only has has generics (not interfaces/type-classes like java/haskell.) It's a language in which you can just write your code without types like a dynamic language, rapidly iterate on it and then add a type signature when your satisfied with it. (That's almost what I do in haskell as well, but I can run into more complex problems.) (This characterisation is probably colored by the period I used java most heavily and might be slighty unfair, but I also limited the description of haskell to the most basic features which it has had since the 90s.) tldr; mmm, delicious global type inference.. [0] http://elm-lang.org http://elm-lang.org
- sdinsn 8y agoA better type system != better software.