main = do args <- getArgs

putStrLn (args !! 0)

Learning Haskell is hard. Those who disagree with this basic premise should stop reading now; they're obviously far more talented and intelligent than I am. However, wrapping your mind around the concepts is just one part of the difficulty. I believe that the syntax itself contributes to the difficulty of reading and understanding Haskell. I think that there are some parts of Haskell's syntax that are meant to be clever and flexible; however, this same flexibility leads them to be difficult to decipher.Unfortunately, I fear the only ones who are going to read this post are the ones most likely to disagree with it, but I'm going to try anyway ;)Function applicationI think "f(a, b)" more clearly portrays function application than "f a b" does. Consider "map myFunc a". It'd be a lot visually clearer which function was getting called and which function was being passed if it were written "map(myFunc, a)".The syntax for curryingI think the currying syntax is terrible. Consider "unwords . map showVal". This is a curry because map takes two arguments. Now consider "unwords . spam showVal". Is this a curry? Who knows? Unless you understand the type signature for "spam", you don't know.I think it's important for the syntax of a language to help you decipher code even if you aren't familiar all the libraries being used. For instance, Haskell does this by forcing you to use upper case names for types, and I think this was an improvement over C. I think you should have to *work* to make a curry happen, and it should be obvious to the reader what is happening. Perhaps "unwords . curry(map, showVal)" would be clearer.Function type declarationsSimilarly, the syntax "f :: a -> b -> c" places the burden of currying on you when it's often the case that you aren't even thinking about currying. It'd be a lot clearer to just say "f :: (a, b) -> c". This doesn't stop you from using currying; it just frees you from having to think about it when you're not actually using it yet.If you actually are writing a function that returns another function, say that! For instance, "f :: (a) -> ((b) -> c)". Now the reader will know that you plan on actually returning a function without the issue of currying getting in the way.Point free stylePoint free style can be elegant. For instance, consider "h = f . g". This says that "h" is "f of g", and it's more elegant than "h x = (f . g) x".However, consider "main = getArgs >>= putStrLn . (!! 0)". When point free style is used in the middle of a larger expression, perhaps with a curry (or in this case a section), a ">>=", and function composition, things can get out of control. I think it's often clearer to use an explicit argument so that the reader doesn't have to figure out where the invisible argument is being consumed. Even though the following is one line longer, it's much easier to read:Point free style is even more inscrutible if no type declaration is given. In such cases, the reader is forced to hunt down the right number and type of arguments.The $ operator"$" is function application. Consider "putStrLn $ args !! 0". This is the same as "putStrLn (args !! 0)", but you don't need the parenthesis. Aside from the fact that you're left wondering which has higher precedence, "$" or "!!", this is an improvement. However, if you have a very long line with a mix of "." and "$", things can get confusing. Worse, you end up reading the code from right to left. It seems strange to have to look at the operators in order to figure out whether to read from left to right or right to left. Sometimes, you're left reading from the inside outward. It's trivial to write a function application operator that has its arguments in reverse, and I think it improves readability. After all, that's exactly how UNIX pipes work, for instance, "cat a | b | c". Furthermore, that's how ">>=" works for monads. My earlier comment about excessive use of point free style still stands, though. If you get too many "invisible" variables, it may be better to use a "let" or a "where".Record member accessI think C's "a.b" is clearer than "b a", especially when you have multiple levels. Consider, "a.b.c" vs. "(c . b) a".Mixing monadsFor new programmers, OOP can be mildly difficult to understand, but it's powerful. Monads are even harder to understand, and they're even more powerful. If you consider "obj.getHouse().getDoor().ringDoorbell()", playing with multiple objects at the same time seems to have linear mental complexity. However, playing with multiple monads at the same time seems to have exponential mental complexity, especially since you can only use the "do" syntax with one monad at a time. If you mix the State monad (for state) with an Either (for error handling), a Maybe (for NULL encapsulation), IO, etc., things get tricky. Hand waving a bit, if it were trivial to mix and match monads, there wouldn't be a need to put IORef in the library--the user could just mix IO and State on the fly.ConclusionIn general, I really like Haskell, so please have *some* mercy on me when you flame me to death for stating these opinions ;)