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 ()
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.
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".
> 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.
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 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.
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.
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.
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.
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.
> 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.
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.
[+] [-] solomatov|12 years ago|reply
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
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
> 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
#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.
[+] [-] swift|12 years ago|reply
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.
[+] [-] sanxiyn|12 years ago|reply
http://ghc.haskell.org/trac/haskell-prime/wiki/TypeDirectedN...
[+] [-] sseveran|12 years ago|reply
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
[+] [-] davorak|12 years ago|reply
[+] [-] IsTom|12 years ago|reply
As for 5., GHC is de-facto standard.
[+] [-] davidw|12 years ago|reply
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
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
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
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
[+] [-] tbirdz|12 years ago|reply
I'd recommend taking a look at the Real World OCaml book: https://realworldocaml.org
[+] [-] seanmcdirmid|12 years ago|reply
[+] [-] unono|12 years ago|reply
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
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
[+] [-] rbanffy|12 years ago|reply
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
[+] [-] Vektorweg|12 years ago|reply
[+] [-] tome|12 years ago|reply
http://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pd...
[+] [-] sseveran|12 years ago|reply
[+] [-] seanmcdirmid|12 years ago|reply
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