Shared publicly  - 
 
Some reflections on Haskell

Now that I've written a few thousand lines of Haskell (mostly an astronomy application using the Snap web framework), I think that I can put up a few reflections on the language.

1. I'm more productive coding in Haskell than in any other language I know.

Until now, my most productive language was Python. I now find that I can code the same level of functionality in Haskell in the same time or less than Python, but the code is easier for me to understand a few days or weeks later.

2. The lack of a professional level IDE is a major barrier.

The full Haskell ecosystem including the thousands of Hackage libraries is huge. Haskell needs a sophisticated IDE to help navigate this ecosystem, incorporating wonderful tools like Hoogle and taking advantage of the many error checking possibilities available in a statically typed language.

I'm used to using sophisticated tools like Eclipse and Visual Studio. There is an EclipseFP plugin for Haskell but it is painfully slow even on my 8Gb quadcore 64 bit development machine. I suspect that it just can't keep up with larger Haskell programs. After much effort I succeeded in getting Leksah to compile under Ubuntu but it seems limited. A Visual Studio extension written a few years back has bitrotted and is not compatible with the latest versions of the GHC compiler.

3. Error messages could be improved.

A typical Haskell error message is a long and often incomprehensible type message. After some experience you can start to get useful information from these messages but it seems odd that such a high level language has such a rudimentary error reporting system.

As just one example, I sometimes make the mistake of returning different types from the if and then portions of an if expression.

A simple message like "If expressions must return the same type in if and then sections." would be very useful. However, instead Haskell seems to assume that the correct type belongs to the if section and returns a long type error for the value of the else section. If the error was actually in the if section type, this can cause you to spend too much time looking at the wrong code.

4. String conversions are painful

The Snap framework mostly uses ByteString. Much other library code uses Text and many Haskell functions expect [Char] values. With a few rare exceptions, Haskell does not do implicit conversions and so I spent a large amount of time tediously adding explicit conversion functions between the three types. I would say that about half the compiler errors I get are related to a missing string conversion function.

Haskell is a beautiful language. It is a shame that my code needs to be littered with these conversion functions. Perhaps Haskell should have a use-at-your-own-risk feature to turn on implicit string conversion? There is such a feature for string literals already.

Despite these warts, I think that it is clear that Haskell is a great language for coding large projects. I expect that the three issues I've mentioned will be addressed at some point soon and then Haskell should be destined for widespread use.
10
Kevin Jardine's profile photoYves Parrays's profile photoKetil Malde's profile photoChaddaï Fouché's profile photo
24 comments
 
Literals, strings or numbers, don't have any implicit conversion per se, unless you consider that it's converting from you source code to a data type. At least in my head, "implicit conversion" is between data types in memory, which Haskell never does anywhere. Also, if your code is littered with conversion functions then you're doing something wrong.

GHC doesn't assume that the "then" section is correct. Instead, it tries to unify the types of the branches. If the "then" type appears on the error message as "Expected" or "Got" is just a small detail =).
 
Felipe, I'm not "doing something wrong". I'm using functions that expect different string-like types as I explained in the post.
 
+Kevin Jardine I mean, ideally you should be doing string conversions at most on the boundaries of your code. If your code is "littered" with string conversions, then something is wrong. Perhaps I'm putting too much emphasis on "littered" when reading your text on my head? =)
 
For instance, most file manipulation functions expect a FilePath, so you may have to do a bit of conversion to and fro there.
 
Hello, I'm sorry to see that you consider EclipseFP as too slow for your usage. Do you have a sample project that we could use as a benchmark? As the maintainer of EclipseFP, I use it exclusively for my Haskell development on a dual core laptop. The latest version (2.2.2) is much quicker that it used to be. The thing that's really slow at the moment is building the project executables, but that's nothing to do with EclipseFP, it's just the linker and there's not much I can do with it.
 
I think that the string problem is likely to gradually be reduced as more library functions move from [Char] to Text. There will always be a need for ByteString or something like it for binary data, but ideally programs that don't need to manipulate arbitrary binary data will be able to deal mostly or exclusively with Text.
 
+JP Moresmau - I have a Snap application file with a fairly large number of lines and there is a noticeable pause for every keystroke I enter as the "Building workspace" message appears in the Progress area . Does your plugin assume shorter files (I could easily create those) or is the problem perhaps that it uses the (large) Snap framework?
 
Regarding string conversions: you can also make a wrapper module that takes all it's parameters as specified data type. Like this:

module Foo (funA, funB) where
... imports ...
makeConversion bs = Data.ByteString.Char8.unpack bs

funA arg1 arg2 = WrappedModule.funA arg1 (makeConversion arg2)
funB arg1 arg2 = WrappedModule.funB (makeConversion arg1) (makeConversion arg2)

The end result would be less clutter as there would be less modules containing string conversions.
 
Why do we need an IDE? I can see arguments for more easier ways to navigate Hackage, possibly with some editor-level integration (when using an extensible editor), but does there actually need to be an IDE for Haskell to be usable?

I've also seen arguments by people using Java, etc. that people need to stop using text editors (e.g. by Gosling: http://www.computerworld.com.au/article/207799/don_t_use_emacs_says_java_father/ ), but I don't see why: when I used Java at uni, I did it all in Emacs, and never went anywhere near the amount of power that JDEE provided (admittedly, the projects were not LibreOffice-level of size and complexity). Instead, that to me argues that the project should possibly be split up into sub-projects or something...
 
+Krzysztof Skrzętnicki Yes, I think that I could create wrapped versions of some of the Snap functions to get them to use Text rather than ByteString. That could make my code cleaner.
 
+Kevin Jardine Are you using the latest release? There was a time where the checking of the file would cause the GUI to slow down, but that should be fixed in the latest release. But of course under the scenes there's still GHC checking the file is correct. However, the syntax highlighting, etc should be pretty fast.
 
+JP Moresmau A few days ago I updated to the latest release via Eclipse Indigo, but the Scion Browser recompile failed. After a Google I reverted to an earlier version of the Scion Browser (but kept the new version of the plugin). No obvious improvement in performance though.

I'm looking forward to trying the latest version from github once it is available as an Eclipse update.
 
+Ivan Miljenovic I admit that I prefer Eclipse or Visual Studio because I use one or the other for most projects. At this point, I'm happy to try something else, although I am heavily biased in favour of something with a reasonably elegant interface. What would you recommend as a syntax aware text editor (under Ubuntu which is what I am currently using for Haskell development)?
 
I'm just using Emacs, too, and it Works For Me. My experience with IDEs is ten years old, and Visual Studio was that it was a lot of annoying crap that mostly got in my way and slowed things down - hopefully, the situation has since improved.

I'm curious how well Emacs scales, though, my projects are all pretty small. Programming-in-the-large for me is when programs are big enough that a single person can no longer keep track of all the bits. I think Haskell is great at making good reusable program fragments and at keeping code compact, meaning you can stretch the programming-in-the-small paradigm a lot further. But what happens beyond that?
 
+Felipe Almeida Lessa I agree with you that my remark in the original post about string literals was not correct. The OverloadedStrings feature does not convert string literals but uses the compiler type machinery to infer what their actual type should be. As you say Haskell does no implicit conversion. And generally speaking, implicit conversion as seen in languages like PHP is a Bad Thing.

Perhaps as I mentioned in an earlier comment the string issue will fade away eventually with more widespread use of Text.
 
+Ketil Malde At Silk we have about 25000 lines of Haskell, which I think goes beyond programming-in-the-small (although I wouldn't call it large yet). Haskell is a great help here, mostly because of the types, and cabal and related infrastructure. Refactoring is painless and almost risk-free, since the type-checker catches almost all errors. It's also easy to create safe interfaces so programmers who don't know the intricacies of your package can't make mistakes. And cabal, cabal-dev and hackage 2.0 make it easy to distribute the packages internally.
 
+Kevin Jardine We use vim or TextMate (Mac only). For vim, I use the excellent hothasktags to generate a tags file, which allows me to jump to function definitions from a usage site. Very useful.
 
+Kevin Jardine I'm gonna open the discussion about error messages: YES, they are cryptic to newcomers but bare in mind that you can't have power without complexity, and the error messages have to be generic and target exactly the point. (Which they do)

You have to get used to them [*]. I just understood recently, after about 3 years of Haskell, the error messages due to improper use of existentials (about skolem type variables and such), most famous example being of course the error you get when trying to do 'runST (newSTRef xxx)'. But the message describes exactly the problem (implementation of runST uses a clever, yet perfectly reproducible just with a common extension activated, trick the error message can't be aware of).

[*] I think most people, when discovering Haskell, suffer from a complexity evaluation bias: they think everything (error messages, for instance) is more complicated than in imperative world, forgetting that it was as much complicated (or more) for them when they begun learning <insert OO/imperative language here>.
Just remember C++ compilers' error messages in case of heavy and tricky use of templates... You're gonna regret GHC's error messages...
 
+Yves Parrays Yves, I don't mind the error messages that are generated. My point (sorry if it was not clearer) is that they are generated from the point of view of a type checker, not from the point of view of a software developer. When I change the type of one part of an if expression but forget to change the type of another part, I create a type error and the error message generated might be useful. But it would be way more useful if GHC pointed out something at a higher level (in this case, pointing out that the types of both parts of an if expression need to be the same).

Other languages do try to make a guess at least of what the real problem might be. They say something like "Did you perhaps forget to .." or "you might check to make sure ..". They speak to the human being creating the code.

Oh and yes, I do expect GHC's error messages to be way better than those from a C++ compiler. After all, Haskell is way better than C++, right? :)
 
One error message that ghc often gets wrong, is when a missing parameter or some such makes ghc look for e.g. a Num instance for Int->Int. In most cases, adding that instance is not what I want. Apart from that, I think errors are pretty good, and that ghc makes an effort to use sensible type aliases and such. It used to be a lot worse.
 
The error messages have become much more useful and readable since I first used GHC, that's certain. I think what Kevin is asking for is more in the nature of an analyze of the context of the error that would propose several alternative ways to correct the problem, and not only the myopic first pick like currently (typically the proposal to define a Num instance for Int -> Int). I don't know how such a thing is implemented in other compilers (Clang does a pretty awesome job there) but I don't think this analyze phase needs to be too tightly coupled to GHC code, basically GHC could print the current error message and if asked to, feed the analyzer with the error and the context to it. Of course I don't know GHC well enough to be sure of that.
And the algorithm would be closer to pattern recognition based on a frequent errors database than a mechanical analyze (contrary to type inference and parsing). In fact, it would probably be more like HLint but with type information.
Add a comment...