Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've been spending time learning Haskell lately, as part of an investigation into tools which are amenable to static analysis at work.

To learn about the situation, I've put together similar programs in Lisp, OCaml, and Haskell, as well as installed compilers for Haskell & Ocaml on the PPC.

Well -

I've coded for over a decade, and I've never encountered such a difficult to use language and jargony community & documentation (including AWK & SED). The only reasons I have been able to do anything are Real World Haskell, Learn You A Haskell, and Stack Overflow.

I'm not going to say Haskell is useless, or has no libraries, etc. Those aren't true. It's also not a bad language because it's weirder than my Blub (Common Lisp). It's a really sweet language, and I think in the hands of an expert, Haskell can dance.

But, I'm going to say Haskell is nearly impossible for an experienced procedural programmer to pick up and go with on the fly. There are a few reasons for my opinion:

* Special operators out the wazzoo. >>= ` ++ :: etc. The wrong 'dialect' of Haskell leads you to believe it's Perl and APL's love child. It's just not clear what something does until you find a reference book. Google doesn't help here - I don't even know the verbal names for some of them. :)

* Monads & in particular, the IO Monad. The number of tutorials and explanations (and the number of new ones) suggest that this is not the most obvious concept in the land. It seems to be very simple if you know what you're doing (and what operators to use), though.

* The REPL is not identical to the compiler. This means that you can't trust the REPL. Coming from Python and Lisp, that is a pain.

* Type messages that are quite unclear, and probably require referring to the Haskell98 report to fully understand.

Regardless, the above are surmountable problems and reasonable when moving to a new paradigm (very frustrating, though).

However, there are two key issues that are close to deal-breakers, with a third more minor one.

* Time to put a small program together. Easily 3x-10x my time working on Ocaml, a language which I am less experienced in (in both languages, I am amazingly inexperienced).

* Building the compiler on PPC (business reasons why I would need to do this). Ocaml builds with the traditional ./configure && make. Very straightforward. GHC requires a cross compile with some funky source tweaks, or possibly a binary package (but the bin package dependency tree required replacing libc++, at which point I stopped). This is a dealbreaker unless I can straightforwardly guarantee my boss a good ROI with Haskell vs. (OCaml or other statically typed language).

* Human costs for my code. It's not professional to have a codebase only I can use in a team. Yes, the team could learn Haskell, but would it be a good ROI? If OCaml gets us there faster...

So Haskell is probably not going to work for me at work. :-( We'll see though.



The REPL is not identical to the compiler. This means that you can't trust the REPL. Coming from Python and Lisp, that is a pain.

I can't agree more. This is by far my biggest issue with Haskell. It would be so much easier to learn the language if you didn't have to learn the REPL separately from the language proper.


This problem with the repl is actually going to disappear in the next ghc release (7.4.*). In particular, as of that ghc release you'll be able to interactively define types and functions (both!) in the repl.


Help is on the way. Support for accepting all top-level declarations in GHCi has been recently added to GHC HEAD: http://www.reddit.com/r/haskell/comments/kmxf2/ghci_now_supp...


I found that one of the largest difference between Haskell and OCaml is the amount of time I spent figuring out how to make code go fast. Because OCaml is eagerly evaluated and that its compiler is extremely simple, I find that the "tools" that I learned in school and in my time coding in other languages apply to making OCaml code fast (and predictibly fast). With Haskell, you need to be more keenly aware of how evaluation is handled, and I guess I have still a lot of things to learn there, because I still have problems making really simple Haskell functions that don't crash.


>because I still have problems making really simple Haskell functions that don't crash.

Do those functions compile, and then crash anyway? I'd be interested to see examples. In my limited experience, if you can get your code to compile, it's pretty stable. Would be interesting to see counter examples.


Non-exhaustive patterns are one thing the compiler can't catch:

  fn 0 = return ()
  main = fn 1
Giving an empty list to head/tail is another:

  main = head []


Speaking of the first example, compiling it with -Wall provides some hints:

  $ ghc --make test.hs -Wall
  test.hs:1:1:
      Warning: Pattern match(es) are non-exhaustive
               In an equation for `fn':
                   Patterns not matched: #x with #x `notElem` [0#]


Wow, that's cool. It even works for non-Bounded argument types, as in fn [0] = 0:

  Warning: Pattern match(es) are non-exhaustive
        In an equation for `fn':
            Patterns not matched:
                []
                #x : _ with #x `notElem` [0#]
                0# : (_ : _)


I'm at school all day, but I'll post something tonight.


OK, so we were learning about greedy algorithms at school and I implemented a very naive implementation of a change making algorithm for Canadian coins. Here's the Haskell code:

    makeChange :: Int -> [Int]
    makeChange amount = loop 0 [200, 100, 25, 10, 5, 1] []
        where loop total coins@(c:cs) solution
                  | total == amount = solution
                  | null coins = error "no solution"
                  | otherwise = if total + c > amount then
                                    loop total cs solution
                                else
                                    loop (total + c) coins (c : solution)
(I could make this a lot better by returning an [(Int, Int)] and by using integer division, but I wanted to just follow the algorithm described in the textbook.)

To make sure that my code was correct, I wrote a QuickCheck property:

    quickCheck (\(Positive n) -> sum (makeChange n) == n)
However, running this after compiling my file with GHC causes a stack overflow and I need to Ctrl+C out of the process.

On the other hand, the exact same algorithm in OCaml runs extremely quick and without a hicup.


quickcheck is running makeChange with an arbitrary Int. maxBound :: Int here is 2147483647. When given a number that large, makeChange recurses a lot, subtracting one two-dollar coin at a time, so you blow the stack. This is where you need to consult a haskell guru to find a way to make your code tail-recursive -- or find a smarter algorithm (using mod c for example so it only needs to recurse 6 times total).

Amusingly, if you simply change the type to Integer -> [Integer], it all works ok. I suspect that since Integers have unbounded size, quickcheck only tests with reasonably small ones.


It's even worse than that if he's on a 64-bit machine!

It runs just fine on my box with i = 2^32: 10s to completion or thereabouts.

However, the way this is written, the code has to construct the entire list in memory before it can print any of it out so for larger lists it is pretty much guaranteed to blow the stack and / or memory depending on the computational representation.

If it was using a snoclist or something then it could stream the output and perform the calculation in constance space, as it stands it has to hold on to the whole list of integers before outputting any of them.

I'm surprised that the OCaML version 'just worked' frankly: either a) the OP didn't use QuickCheck with their OCaML code or b) the OCaML QuickCheck doesn't bother testing across the whole Int space.


Also, a quick test reveals that quickCheck on an Int will by default test 100 Ints across the entire range up to Int::maxbound. If your Ints are 64 bit this really isn't going to work very well on this code, regardless of what language you write it in unless you can stream the output. Any code that holds on to the list is going to fall over, since the size of the list is going to exceed physical memory for larger test values.


The code is already tail recursive, which is why it's doubly puzzling. Also, like you said, using an Integer instead fixes the problem. But I find that fixing these issues distracts me away from the main problem and that doesn't happen in OCaml.


Special operators out the wazzoo...Google doesn't help here - I don't even know the verbal names for some of them.

Operators are just functions, so try Hoogle:

http://www.haskell.org/hoogle/?hoogle=%3E%3E%3D


FYI: Hoogle dies on :: and `, and gives a wrong result for =>.

Please note that this is just a simple problem, with a solution of printing out the right reference cheatsheet.

The real difficulty comes (IMO) when looking at piles of symbols in code and trying to determine what kind of meaning is coming from the symbol soup (C++ and Perl are notorious for this too).

Quite often (usually?), of course, public Haskell is written in a very clear and readable style. That's a major reason to use Haskell - to write in a readable language.


You won't find :: and => in Hoogle because they're keywords, not functions. For keywords, see the the wiki page that gtani posted:

http://haskell.org/haskellwiki/Keywords

I recommend reading Learn You a Haskell instead; the keywords were second nature to me by the time I finished.

I agree that there's too much "symbol soup" Haskell out there that uses infix functions excessively. Even if you recognize all of the functions, you still need to have their precedences memorized to decode the soup.



(too late to edit above)

The haskell wiki link is not adequate, relative to the RW Haskell hard copy index (not avail. online unfortunately), which starts with 1.3 pages of symbol function names (and is missing a few relatively common QuickCheck symbol names).

I was looking for something like the Scala staircase book, first edition freely available online, which has a complete list for that language.


Whatever symbols you're seeing in RWH that aren't on that wiki page are probably functions, not keywords. For functions, you should use Hoogle.

For better or worse, there will never be a complete list of infix functions for Haskell because new infix functions can be defined by the user.


I've had very similar experiences -- especially with the operators.

I'm also nervous about the "DLL Hell" the article mentioned. I want to be able to build my programs for the next 10 years without having to worry about dependencies going away.

I believe this is because cabal doesn't generate a manifest of the exact versions your library/application depends on (unlike Ruby's bundler's Gemfile.lock file).

Hopefully this gets resolved soon.


//would it be a good ROI? I am only a haskell beginner but i think we can break down this question a little more specific by talking about exact timespan of the projects. From what i understand haskell's maintainability is a huge advantage in some projects with never ending specification changes and feature requests.(ERP??) Anyone has tried haskell for something like that? or were stopped by the chicken-egg problem of finding ppl to maintain the code?? Would be good to have some data points.


The key reason I am looking at this kind of technology is because I have a large block of code in a situation where it is very difficult to do functional or unit tests due to system design. Being able to write in a language that statically analyzes my code for all the errors it can before my poor customers encounter it sounds like a huge win. The maintenance goal is being able to catch my fat-fingers and design flaws prior to rollout.

* I suppose if I had to quantify the maintenance timespan, I'd make a WAG of 5-7 years, possibly 10.

* It's also probable that after its solid now, in 5-7 years I will be doing other things and unable to be reassigned to work on this full-time. So it has to be other-people-hackable.


It sounds like other-people-hackability is the real problem for you then. i guess it's hard to convince someone from other programming paradigms to spend the effort and time required to get proficient in haskell.


Does the lack of multithreading in Ocaml matter for your applications, I think that is the only thing that would push me towards Haskell over Ocaml.


In this particular domain, not very much, due to the amount of IO interaction. In my opinion, the Ocaml threads library would be sufficient, at least for a while.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: