top | item 6190005

Why functional programming? Why Haskell?

63 points| tosh | 12 years ago |book.realworldhaskell.org | reply

83 comments

order
[+] solomatov|12 years ago|reply
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.

[+] quchen|12 years ago|reply
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.

[+] enigmo|12 years ago|reply
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 ()
[+] swift|12 years ago|reply
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.

[+] sseveran|12 years ago|reply
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.

[+] gonnakillme|12 years ago|reply
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".
[+] davorak|12 years ago|reply
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.
[+] IsTom|12 years ago|reply
You can use operators to deal with 3. do c <- openDbConnection =<< readConfigData

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

[+] davidw|12 years ago|reply
> 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'.

[+] rtfeldman|12 years ago|reply
> 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.

[+] mbrock|12 years ago|reply
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.

[+] wunki|12 years ago|reply
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 :)

[+] dfeltey|12 years ago|reply
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.
[+] tbirdz|12 years ago|reply
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

[+] seanmcdirmid|12 years ago|reply
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.
[+] unono|12 years ago|reply
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.

[+] jacquesm|12 years ago|reply
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.

[+] tome|12 years ago|reply
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.
[+] rbanffy|12 years ago|reply
> 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.

[+] VMG|12 years ago|reply
Why do you think aren't those languages more popular?
[+] sseveran|12 years ago|reply
People about to hate on haskell...go
[+] seanmcdirmid|12 years ago|reply
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.

[+] ExpiredLink|12 years ago|reply
Keeping a critical distance is the best reaction to a frenetic hype that acquired religious characteristics.