back
83 comments
Haskell is badly designed language in many respects which promotes ugly code. I think, it won't be mainstream statically typed functional language in any time.

Here is my list of bad language features:

1. It doesn't support dot notation for records.

2. Record names can't be overloaded.

3. Monads might lead to messy code due to verbose syntax. For example, instead of writing c <- openDbConnection (readConfigData), I have to write ugly code

  do
    conf <- readConfigData
    c <- openDbConnection
The situation becomes worse, the more side effecting parameters you need for a function call.

5. Many extensions, essential for productive development are out of the language standard. For example, multi param type classes, existential types. Haskell' standard is in progress for a very long time, and still isn't finished.

I'm not sure what you mean with "do notation for records", but yes, there are some problems with record notation. Lens/zipper libraries can help with that though. (Overloading record names would be sweet in any case.)

> Monads might lead to messy code due to verbose syntax.

Your example could be written `do c <- openDbConnection =<< readConfigData` (assuming there should be a `conf` in the third line). If you need multiple parameters passed like this you're right, you probably need to execute them separately and give their results names. That however can also lead to more readable code if getting the parameters isn't just a single statement.

> Many extensions, essential for productive development are out of the language standard. There is always this discussion what "valid Haskell" is, on the one side the "Haskell Report" guys, on the other the "whatever GHC accepts for the next 10 years" section. A few remarks:

- GHC is a giant project that uses neither multi-parameter typeclasses nor existentials.

- I don't think language extensions are ever removed from GHC, just pronounced deprecated. (I don't know about such a case at least, correct me if I'm wrong)

- Heavy use of language extensions does mean GHC lock-in. It's free software, but I can still see how that could be a concern.

I previously agreed with complaints #1 and #2, though at some point over the last few years this has stopped being a problem. It's not even Stockholm syndrome, I just don't use records that much anymore. The various lens packages can help with record overloading and dot indexed fields, but without some compiler support it will remain a sore spot for people who want/need a better record system.

#5 is a fair to a point. I don't find MPTC or existential types to be useful in almost any case: I actively try to avoid both. ScopedTypeVariables, FlexibleContexts, FlexibleInstances and potentially RankNTypes and the poorly named UndecidableInstances should be standard though (imo). Some people also find OverlappingInstances and the like to be generally useful but you probably don't want to hear what I'd say about the subject.

There are plenty of other (also subjectively ugly) ways to write #3. SHE (a Haskell preprocessor) deals with it in one way, see the pigworker's idiom brackets: https://personal.cis.strath.ac.uk/conor.mcbride/pub/she/idio.... Without a preprocessor you can certainly use applicatives or some regular monad combinators to git'r'done but a little sugar could go a long ways. I'm not attached to any of these.

Lucky for us there are many languages to choose from, personal taste has proven a fickle mistress.

    import Control.Applicative
    import Control.Monad

    data ConfigData
    data DbConnection

    readConfigData :: IO ConfigData
    readConfigData = undefined

    openDbConnection :: ConfigData -> IO DbConnection
    openDbConnection = undefined

    openDbConnection2 :: ConfigData -> ConfigData -> IO DbConnection
    openDbConnection2 = undefined

    openDbConnection3 :: ConfigData -> ConfigData -> ConfigData -> IO DbConnection
    openDbConnection3 = undefined

    main :: IO ()
    main = do
      -- it does work...
      conf <- readConfigData
      _c0 <- openDbConnection conf

      -- how about a flipped bind?
      _c1 <- openDbConnection =<< readConfigData

      -- or join/fmap...
      _c2 <- join $ openDbConnection <$> readConfigData

      -- need two parameters? liftA2/liftM2 has been around for a while
      _c3 <- join $ liftA2 openDbConnection2 readConfigData readConfigData

      -- or use Functor/Applicative to deal with arbitrary numbers of side effecting parameters, longhand idiom brackets...
      _c4 <- join $ openDbConnection3 <$> readConfigData <*> readConfigData <*> readConfigData

      return ()
1. Don't see the appeal.

2. Definitely agreed.

3. "readConfigData >>= openDBConnection" can be used in a simple case like your example. Cases with more arguments can use "ap" or "<*>", which are perhaps a bit ugly, but most of the time you don't need them.

5. The fact that Haskell is a living language that is improving all the time is a point in its favor, I think.

I do think 2 is a real problem, but it seems Haskell people couldn't agree on the solution. Simon Peyton Jones (Haskell 98 standard editor) proposed a change but many people didn't like it.

http://ghc.haskell.org/trac/haskell-prime/wiki/TypeDirectedN...

FWIW, there is currently a GSoC project supervised by SPJ that is working on making overloadable fields available in GHC.
1. Nonissue in real life.

3. Don't modify parameters. It makes code easier to test in any language.

5. GHC may as well be the standard. Are you really going to use a different compiler.

>3. Don't modify parameters. It makes code easier to test in any language.

It's not about modifying parameters. It's about verbosity of the code. The type of the parameter is IO String.

>5. GHC may as well be the standard. Are you really going to use a different compiler.

It's a real issue. Different language extensions might modify how type inference works. If you add one more extension, you program might suddenly stop to compile. We need a fixed set of language extensions which are always available.

> If you add one more extension, you program might suddenly stop to compile.

Do you have some real examples of this happening with GHC extensions? I haven't run across one in the field.

> We need a fixed set of language extensions which are always available.

This sounds very much like a language standard. Haskell isn't much different than other languages here, though the language committee is slow to ratify generally accepted extensions into the standard. GHC at least makes them available to people who want to kick the tires. Then read about closures in Java. Look at C++11 revisions and notice that sometimes meant cutting features, even if they were previously implemented by some compilers (template exports) or prototyped (concepts).

3. in general haskell is even terser then i like.

5. What on earth are you talking about? Conjecturing about type inference is not useful. While I may prefer some extensions to be on be default, in hundreds of thousands of lines of haskell code I am yet to see adding an extension break existing code. The worst I have seen is adding an extension slow down compilation.

In haskell you have to forget most of what you have learned in order to be successful. Otherwise you will fight the language.

> The type of the parameter is IO String.

No, the type of the parameter is String. IO String is the type of the expression that gets that parameter when executed. There's a popular quote by shachaf, "getLine :: IO String contains a String in the same way that /bin/ls contains a list of files" that illustrates this nicely.

You can write the example in your third problem as "readConfigData >>= openDbConnection". This is, in fact, something like what your code desugars to. "do" notation is a useful crutch for difficult code; it's not fair to use that as an example of "verbose syntax".
The code:

    do
      conf <- readConfigData
      c <- openDbConnection
Or just

  do
    c <- openDbConnection =<< readConfigData
    -- do something with c
Where `=<<` can be thought of an infix apply for monads.
You can use operators to deal with 3. do c <- openDbConnection =<< readConfigData

As for 5., GHC is de-facto standard.

> Since pure code has no dealings with the outside world, and the data it works with is never modified, the kinds of nasty surprise in which one piece of code invisibly corrupts data used by another are very rare.

If you're trying to sell me on Haskell, right there you just made me think "uh, ok, guys, but my code does not live in an ivory tower, and very much needs to deal with the real world, messy data, users, and so on".

I realize that Haskell can handle that, too, but in terms of copy writing, it leaves something to be desired.

They subsequently go on to write much more interesting reasons why you might consider Haskell, including examples of companies using it in the real world, but they should not lead with talk of 'purity'.

> in terms of copy writing, it leaves something to be desired.

Haskell advocacy is high in the running for the least effective in the business. Boosters can't seem to help but pitch the things they care about rather than the things the listener does. Maybe it feels satisfying, but it's no way to attract converts.

A more effective approach would be to choose common tasks people perform in other languages and demonstrate how they can be less (error-prone, time-consuming, verbose) in Haskell.

For instance, I recall reading about a Haskell Web framework where the compiler guarantees user-generated content never makes it into any served HTML unsanitized. What a boon for security! However, this is not shouted from the rooftops; instead, it's taking me so long to track down I've given up in the hopes that someone will post it here instead.

You are searching for Yesod - http://www.yesodweb.com - which has some other fancy features like making sure that you can never render an incorrect dynamic URL (because URL's are represented by data types) and more such features.

I've used Yesod in actual real-world projects and it was always a pleasure, albeit sometimes slightly complicated - the new version (which I haven't used yet) promises to simplify this a lot though.

Thanks!
I think it's important to emphasize that while Haskell provides purity, it also enables the definition of impure code in a clear and distinct way, the idea being to encourage a clean "separation of concerns" between logic and interaction.

It's not really that Haskell removes impurity, more that it adds purity. The "main" function of any Haskell application is an IO action, but you can define pure functions which relinquish IO and global state. And the best practices include doing that as much as possible, which is a terrific way of making your program more comprehensible and correct.

I wonder if it would be possible to develop an idiom for programming in a language like C that separated those concerns. Put most/all application logic into const/pure functions, and then have a separate set of interaction functions, i.e. just pull out that one bit of Haskell philosophy from the rest of the language design.

Speculative guesses as to what might stand in the way:

1. The lack of a purity-tracking mechanism like Haskell's may make programming in this style in C more complex and error-prone.

2. The fact that C compilers don't expect you to use this programming style may mean they don't optimize common operations in the way Haskell does; for example, if you do manipulations on large arrays in a functional rather than imperative style, Haskell will optimize away a lot of the temporary arrays, but C compilers may not.

I am currently trying to get some of this working in a library I call 'libalgae' in which I'm encoding algebraic data types and their eliminators (similar to how Epigram works, like an induction principle for each datatype: This allows us to rule out non-terminating programs and ensure coverage of all constructors.) I'm not very much concerned with optimization, as I'd like to use this "DSL" as a bootstrapping language for a self-hosting programming language which provides the same in a more natural way.

The way I've done it so far is seems to be working, and I've implemented standard types (their introduction and elimination rules) and few functions on them. The two main problems I've run into is memory leaking and portability.

Since the constructors of an algebraic data type should return const data, it's a bit difficult to free them without violating the type system. I haven't decided yet if I'll end up violating the type system or just drop that const. I think the latter makes more sense, since we're trusting C as little as possible in the first palce. Everything actually might as well be done in void pointers but I think there's some value in using types where possible for documentation's sake.

About portability. The main thing here that I ran into was that there's no real standard and portable way of doing closures or anonymous functions in C yet. Right now, what I'm doing is using nested functions, which works in GCC and clang, but I have no idea if they'd work in MSVC or any of the other compilers out there like pcc and lcc. OS X has blocks, but I figure if they're using gcc too, why not stick with nested functions. As far as I can tell, I won't have the problem of having nested functions returning before their parent does because of the eliminator model.

The libCello (http://libcello.org/) author has been able to successfully model typeclasses in C, so I will be picking that up as soon as I get around to it.

I pushed some of the code to GitHub just now for this post: https://github.com/guerrilla/libalgae

I'm glad to hear other people had the same idea. If anyone has any suggestions, please let me know, as I wouldn't mind some collaboration on this.

Since beginning to see the value of purity, I write imperative code in a different way. I don't care much about local manipulation of state for algorithms and such, but I write a lot more pure functions.

It's not only that purity is safe and beautiful. It's also in many cases much easier to understand, because it's about writing functions that express transformations. These transformations should make sense in a conceptual way.

When you distill your domain concepts (see "Domain-Driven Design") and write pure functions on domain data, the result is remarkably clear, testable, and debuggable, no matter which language.

Previously I would have been (prematurely) concerned with the efficiency of in-place updates, but since I'm not writing high-performance CPU-bound software, I don't care about that anymore until the profiler tells me to.

GCC's pure and const function attributes pretty much allow for these optimizations.
Absolutely it would, but it's much easier with a type system to guide you.
The power of Haskell is that it acknowledges the fact that it's a messy world and deals with it with the help of Monads.

You could turn it around and say that non-haskell programs live in an ivory tower and ignore the messy world below :)

I downvoted you because you're putting Monads on a pedestal and misrepresenting their purpose. When you mean IO (or perhaps ST and State) please don't say "Monads". Remember that [] is an instance of Monad too, and it has nothing at all to do with dealing with the messy world. A monad is just a typeclass with two operators that behave according to certain identity and associativity laws, and a _lot_ of types fulfil them.
> it's a messy world and deals with it with the help of Monads.

So someone has a tough problem dealing with the messy real world, and then you tell him to use Monads. Now he has two problems :-)

Ok, I'm kidding, but mbrock's answer is a lot better in terms of selling the language in that it mentions something that anyone can figure out is a good idea, without getting into the details of it.

> encourage a clean "separation of concerns" between logic and interaction.

I do understand your point, but purity is one of the defining characteristics of Haskell, and one of the features that differentiates it from most other languages, so I think it should be mentioned up front when introducing Haskell.
Mention it, maybe, but pointing out how great it is not to have to deal with that messy "real world" is bad marketing. It makes you think "oh, so all these benefits only exist in the magical fairy land and not in the real world where I work?"
I understand what you're saying. It would be better to just talk about the enforced separation between pure code and code with side-effects, and explain that it's possible to structure a normal program so that it's mostly pure.
It doesn't say you don't have to deal with 'that messy "real world"'! It is called Real World Haskell after all.
If you're not quite ready to whole hog on the functional purity aspect, I'd suggest you take a look at OCaml. OCaml supports many modern programming amenities, such as Garbage Collection, Higher-order Functions, Static Type-Checking, Generics, Immutable Data Structures, Algebraic Data Types and Pattern Matching, Automatic Type Inference while still allowing for imperative programming when you need it.

I'd recommend taking a look at the Real World OCaml book: https://realworldocaml.org

Doesn't Scala support the exact same features? I know OCaml is based on ML and is mostly structural, while Scala more of a Java-like curly brace lang that is nominal, but your feature list makes them sound the same.
Things Scala lacks which OCaml has:

1. Tail call elimination 2. A full-featured module system 3. Functors (functions at the module level) 4. First-class modules 5. GADTs 6. Polymorphic variants 7. A decidable type system 8. ... more?

Things OCaml lacks which Scala has:

1. Traits 2. Implicits 3. Trivial Java FFI 4. Seamless syntactic macros (c.f. camlp4) 5. ... more?

Having programmed at least 10k locs in all major paradigms, I've come to the conclusion that relational programming (prolog and sql) are the best. Haskell talks a good game and a lot of OO procedural code has been written, but for programming succinct, elegant code, which also happens to be the most lucrative - sql databases underlying most business - the relational paradigm cannot be beat.

It's the most beautiful invention of computer science, Programming in prolog, especially, is the closest a programmer can come to achieving a state of nirvana. A place where the mind is at one with the problem at hand.

Good luck controlling your ABS system using prolog.

Having programmed more than I care to remember (since we're establishing credibility by tossing unverifiable facts in there) I think it takes the right tool for the right job. Sometimes that's prolog, sometimes that's assembly and sometimes it's something else entirely.

Every language that is in use today has a niche, no single language manages to span more than a few such niches and the larger the impedance mismatch between the problem and the language the more work you have to do.

Prolog can be used in embedded systems http://www.hercsmusicsystems.com/
Relational programming is indeed excellent, but how can you put up with writing in SQL? Of all the programming languages I've ever used, it's the one that allows the least abstraction.
Abstraction in sql are views, done with left joins (rules in prolog). It doesn't seem like much, but that's the beauty of of it, you only need one concept and that's all you need. Contrast that with other languages where you have hundreds of little functions.
> Having programmed at least 10k locs in all major paradigms

I don't want to sound insulting, but 10 KLOC is not nearly enough to get a reasonable understanding of even very concise languages. 10 KLOCs of APL, maybe, but certainly not with C, Java (certainly not with Java) or even Ruby or Python.

Beware of Maslow's hammer.

Why do you think aren't those languages more popular?
Sql is extremely popular, that's the LAMP stack. Rails and Django led people astray for a while but that's ending.

Prolog should be more popular, it should be at least embedded in every program, even in the browser.

If there are more than a few for loops or if else statements than the majority of the program should be encoded in sql/prolog.

If you look at all these javascript frameworks, the core concept they're missing is relational programming. The program should be a loop that takes all input events like mouse clicks, etc. and then queries a prolog engine about to what actions to take.

It's getting more popular steadily. Map/reduce is being replaced/complemented with distributed sql, and the frontend will also come around as people starting embedding prolog into their guis (LAMP stack in the browser).

"Why Haskell matters" is a way better explanation than this, i think.
People about to hate on haskell...go
Hate is a feeling I reserve for corrupt politicians, taxi drivers who ignore me, the guy who whistles in my open office, and...C++.

Everything else is either interesting or not. I think Haskell is interesting from an academic research perspective, but I'm not interested in using it to write programs.

Keeping a critical distance is the best reaction to a frenetic hype that acquired religious characteristics.