My rant about Haskell

I spent the last couple of days trying to get the libguestfs Haskell bindings in a better shape, and by and large they are now mostly working for simple cases.

But … how I’ve come to dislike Haskell in the process.

I should say this is not just because I’m a big fan of OCaml and other ML derivative languages, ie. fast and useful functional programming that’s very practical. There are some real problems with Haskell which make it less than useful as a real programming language.

Significant whitespace. It’s not just very difficult to understand how the whitespace works (far more so than Python, where it’s merely annoying), but it also makes it almost near impossible to automatically generate Haskell code, which is what we do in libguestfs.

The IO monad. Most Haskell examples use the IO monad, which serializes everything, making the code the same as more ordinary languages. The disadvantage is that monads are obscure and hard to understand. The advantage is .. unclear: your code is all still serialized, mostly, as well as being slow because of the overhead, so it’s not clear what the point is.

Unexpressive FFI. After dealing with a lot of FFIs I think I’m qualified to talk about this one. Haskell’s is terrible: The documentation is obscure verging on bad. The examples are rare (for anything that’s more complex than calling “sin”). There’s a great deal of brokenness in major features, eg. passing or returning structures. A lot of stuff is simply not possible without delving into the depths of compilers. It would have been much better to define a C API and write FFIs in C.

Laziness .. should not be the default. It’s not how any real computers work, or have ever worked, or are likely to work in the future.

Lack of optional/labelled args. Everyone else has them. Haskell has a huge hack. (If you try to implement this huge hack in reality you’ll see it’s not practical if you have a large number of functions that want optional args).

Also I get the impression from reading online that Haskell is widely studied and often pimped, but not used very much in reality.

21 Comments

Filed under Uncategorized

21 responses to “My rant about Haskell

  1. christoph höger

    You might be right about the more or less technical points (ffi, whitespaces and optional args etc.), but ranting about the core features of haskell is a bit pointless.

    The advantage of IO Monad & co. is very clear to everyone involved into language semantics. Short: It makes it possible to handle side-effects. If you don’t care about safe code, choose another language.

    The problem about laziness is also none. It might confuse you sometimes, but it is exactly the way computers dealt with infinite problems for ages. And it’s pretty efficient, too.

    This seems to be like ranting about python not having a static type checker or c not being usable in a proof checker. Since you are interfacing with the language I propose to accept it’s unique selling points and file bugs against the technical problems you encounter.

    • rich

      Well, that is rather a large assumption since I am extremely interested in how we can write safer code [and multiple other examples].

      But do please tell me about how the IO monad is helping to improve code in the real world.

      • Sam Schlegel

        If you have another way to describe and compose impure actions in a completely pure language, I’m sure the Haskell community would love to hear it.

      • Federico Lebron

        Hi 🙂

        I don’t have that much experience with Haskell in the real world, but an easy advantage I can see is that, if you know your code does not use ugly things from IO, you can test it separately from other code, and know that it will work exactly the same way. There will be no line of code you didn’t see/cover in tests which accessed a “global variable”, or made some connection somewhere, etc. In isolation, the module you are testing will work the same as when you integrate it within your system, if it is pure.

        Stealing a quote from Guy Steele, “…some people prefer not to commingle the functional, lambda-calculus part of a language with the parts that do side effects. It seems they believe in the separation of Church and state. :-)”

        As for optional arguments, isn’t Maybe the go-to typeclass for “This thing could be here, or it could not?” For an optional parameter, you could pass Nothing, or Just param… but yeah, that doesn’t solve named arguments. You could pass a map, but that’s not pretty.

      • rich

        “If you have another way to describe and compose impure actions in a completely pure language”

        or in other words (for anyone using a normal functional language) “blah blah blah blah blah your all rong blah blah”.

        This means nothing to ordinary programmers in the field. And ordinary functional programming languages already do this fine.

      • christoph höger

        As others have already pointed out below: It separates state from pure code. As such a static analysis can rely on certain assumptions. For instance this is why lazyness in haskell is “correct” (giving the same result as call-by-name modulo termination) and lazyness in scala is not. It simply boils down to the fact that you cannot make certain mistakes in haskell. I guess super 133t h4x0rs like yourself ( 😉 ) do not tend to make such mistakes, but average joe programmer does. Finally, I do not know what exactly makes up your real world. But my real world does contain things like proofs an d it really helps when a language has clean semantics.

        Disclaimer: I actually do not use haskell a lot for different pragmatic reasons. I just don’t like programmers rant about languages they do not understand/use.

      • matejcepltest

        @christoph höger You seem to assume that there is at least one regular joe programmer using Haskell. An interesting assumption.

  2. Regarding whitespace: you can use braces and semicolons; this is part of the language standard (Section 2.7) precisely to ease program generation.
    Regarding monadic IO: there are several modularity and safety advantages to segregating side-effects. For example: safe uses of software-transactional memory (STM) can be tracked through the type system.

  3. Random

    >almost near impossible to automatically generate Haskell code
    That’s incorrect. You can always use {} and ; if you like to. This makes whitespace insignificant. Haskell supports BOTH.
    You may want to checkout template Haskell for Haskell code generation and she for a non-trivial Haskell preprocessor.

  4. Whitespace: Haskell has optional braces and semicolons. Use those if you’re generating code.

    IO Monad: I don’t know what you mean by “serialized”. In any case the IO monad means that side-effects are statically typed and that actions are first-class. It’s common to use unsafePerformIO in FFI code when the bound library functions are known to be pure.

    FFI documentation: Have you read Real World Haskell’s chapter on it? http://book.realworldhaskell.org/read/interfacing-with-c-the-ffi.html

    FFI as C API: That might not be portable to other compilers or for binding to other languages. It might make sense from your perspective (I gather you don’t know much Haskell, but do know C). For me, the FFI was a pleasant experience, and it integrated nicely with what I already knew (Haskell). By contrast, this was not my experience with Ruby’s C based library bindings API (in 2008; I don’t know the current situation there) where I had to learn a lot of C and CRuby internals just to get started. I suspect that doing that with GHC/Haskell would be even worse because the language is so different from C, and GHC isn’t itself written in C. If you’re the author of a C library looking to write bindings to other languages, a C API would probably seem preferable to you, but the typical author of bindings in Haskell is a Haskell programmers looking to integrate existing libraries.

    Laziness: OK? Making a pure lazy language was more or less the original goal of the research language that was Haskell in the early days. It has since proven really useful in a number of situations, to a number of people. It makes possible a whole slew of elegant ways to solve different problems. It does introduce new problems as well, but that doesn’t mean the idea is useless.

    Optional/labelled arguments: They don’t compose well in point-free style, but when you need them just use a record. That gives you a reusable and documentable first-class capture of your arguments. You call this a huge hack, but I don’t know what gives you that idea. Records are pretty standard. I suspect your problem is that you’re writing bindings from Haskell to an API designed in a very different language and heavily relying on optional and labelled arguments. In that case the defaults for the optional values are probably already handled in the bound API, so you could use the Maybe type for those arguments. As for labelled arguments, do you really need them? Can’t they be positional? If not, and if you’re already generating code, making a few records to hold the argument keywords doesn’t seem like such a big deal.

    • rich

      FFI: Yes, I’ve read it a lot. libguestfs passes structs in both directions and much help is required if I’m going to make it work.

  5. You may want to consider watching Brian Beckman’s “Don’t Fear the Monads” talk. He gives a great explanation of Monads which justifies their use. In short, it’s a way to build complexity out of simplicity. That’s the recurring theme of Haskell and similar functional languages. http://youtu.be/ZhuHCtR3xq8

    But I do agree with you on one thing. Haskell’s documentation does kind of suck. I’ve been tempted to switch to Erlang many several times just because it has better documentation for the machine learning / database work I do. I shouldn’t have to rely on the good people at StackOverflow to help me with every rudimentary task. Where in the hell are the code examples and PDFs…

  6. This sentence sums up why I dislike Haskell:

    “If you don’t care about safe code, choose another language.”

    Part of a language is the community of people who use it. Sentences like that indicate that the Haskell community puts far too much weight on, well, let’s be nice and say qualities that are grating to interact with.

    Besides, we already have a language community for socially abrasive jerks. And the documentation for ruby is far better.

    • Mathnerd314

      Sentences like that keep the community “small” and centered on its purpose. It’s like kicking people off your mailing list because they only post off-topic stuff; it’s not pleasant, but it has to be done. If you try to keep everyone in, the quality of the community (and the language) declines to the point where you don’t really have a community.

      • Zoltan

        That’s why Haskell will never be a prime language. Look at Clojure and its success of late. That is how a community is built around a fine language.

  7. Significant whitespace in Haskell is optional. Many of my code generators output braces-and-semicolons Haskell for simplicity.

  8. The beauty of the IO monad is not that it improves IO but that it improves everything else.

    Because IO can only occur within the IO monad, you can be assured that every non-IO expression is free of side effects and independent of the state of the outside universe. Real-world programs can therefore remain almost entirely “pure” and easy to reason about. The small amount of “effectful” logic that they require can be cordoned off by bright IO types and safely isolated by the type checker.

    Thus for the small fee of telling the compiler where you truly must do IO, you get a big discount on all of your non-IO logic, which accounts for the bulk of most programs.

  9. Well, it’s sad that this post fails across the board, although I do share the aversion to the FFI documentation (not the FFI itself).

    However, It’s worse that not many people seem to get the point of the IO monad. Of course one of the points is that it encapsulates real world side effects and sequences them, but it’s not the main point. In fact I’d say it’s secondary and not the reason why Haskell programs are safe.

    Did you ever notice that even code involving IO is comparably very safe in Haskell? Let me tell you more about that.

    The big advantage of IO over most other side-effecting abstractions is that actions are first class, so you can write combinators for them. Why is Haskell code short, readable, and why are even side-effecting programs very safe in Haskell? Because you can write to-the-point and type-safe combinators, which not only makes code more readable, but also centralizes the responsibility for many classes of bugs:

    main = forever (putStrLn “Hello world!”)

    The nonstrict nature of (>>=) is crucial for this to work. Laziness and IO give you macros on steroids without introducing special language features. The core of the language couldn’t be simpler. What could be simpler than that?

    Another point that is completely wrong in the post is that IO makes things slow. In fact a benchmark reveals that IO is actually very lightweight, in many cases even lighter than calling a series of side-effecting functions in C. The reason is that internally there is no fixed calling convention and code is reached in a very natural way. When you write “x(); y();”, then x() will return and the caller will call y() afterwards. Why doesn’t x() jump to y() directly? In (GHC) Haskell that’s what happens in many cases. This also gives you very fine-grained control over parallel processing, and you don’t even need to encode it by hand!

  10. Xei Chung

    Im glad to see your points of view on the IO monad.
    A haskell fanatic would never understand this because they think programming as an application of mathematics. So they see side effects as something incompatible with the “pureness”. Later they say; “hey, but we need to do side effects… at the same time of being pure. Solution: we have the IO monad which is NOT part of the language, so the language itself remains pure”.
    This is absurd for programmers who don’t see programming through the mathematical model. But makes sense for the others.
    I personally found the IO monad as not the best example of what a monad can do. The maybe monad on the other hand is incredible elegant.

  11. Rich, First of all, some of the comments here don’t represent the Haskell community. Haskellers welcome and support newcomers. Your complaints are very legitimate and can be answered in simple terms.

    So, indeed, you are free to use braces and semicolons instead of meaningful whitespace. Although, if your goal is to generate Haskell code programmatically, you might want to look at Template Haskell.

    All Haskell programs are monadic at the top. main itself is of the monadic IO() type. That’s because all programs have to do I/O at some point. Think of a Haskell program as a huge printf, which deals with all the output (as well as the input). It’s when you’re executing this huge printf, you have to call functions that produce the values you want to print. It’s these functions that are pure and they usually form the bulk of your Haskell program and libraries.

    Haskell distinguishes between functions that do I/O and those which don’t, so you always know whether you’re implementing the printf, or pure calculations. Why is it important to know exactly which functions do I/O and which don’t? Yes, it helps in analyzing the behavior of programs, but there is a much more important aspect of purity that has been gaining more an more traction in recent years. This is actually the main reason I switched from C++ to Haskell: Concurrency and parallelism.

    If you want to spawn a thread to run a function, how do you know whether it’s safe? I’ve done this in C++ many times, and it still scares me. How do you know that, when you are modifying any variable, another thread isn’t in the middle of reading that variable? I know, if you do synchronization right, you won’t have data races. But figuring out the science of data-race avoidance is harder than learning Haskell — I am not joking.

    What’s the Haskell solution? A pure function has no side effects so data races are impossible! Because of monads, you can tell from a function signature whether it has side effects or not, and if it does, of what type. This is why concurrency and parallelism are so easy in Haskell. You can actually concentrate on performance rather than keep chasing bugs.

  12. Pingback: Everything Sucks — Jamie Forrest

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.