What are the challenges that an experienced programmer with the usual C++/UNIX background would face when trying to learn functional programming? The main motive for learning is to gain a fresh perspective, perhaps that'd help write better code. This is debatable though.

There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs.
If this question can be reworded to fit the rules in the help center, please edit the question.

6 Answers
6

When I started digging deep into Haskell (coming from a mostly Basic/C background), I already had a firm grasp of closures from thinking about them for months and months, and using them in JavaScript. Whether or not this is the biggest hurdle of learning functional programming, I'm not sure, but I'd imagine they're easier to understand when state is taken out of the equation.

I suppose the first major hurdle for me was the syntax. When I first took a look at the xmonad source code, it looked quite humorous:

The arrows pointing here and there, and things like maybe (return Nothing), gave me the impression that they make it up as they go, and that the syntax has zillions of operators and keywords (custom operators were a foreign concept to me).

The next hurdle was Haskell's type system. The nice thing about this one is, if you don't quite get it, the compiler will be quick to point it out :-)

Then, monads. I suppose my breakthroughs in understanding them were:

do {x <- m; k x} is equivalent to m >>= k

Monads are merely a design pattern. They are not given special treatment by the language (other than do notation), and one does not have to understand category theory to use them.

Monads, functors, etc. are all, in an abstract sense, "actions" yielding a "thing".

With a Functor, you can apply a function to that thing, but you don't get to unwrap it (e.g. (*5) <$> Just 2 = Just 10).

An applicative functor lets you take two actions and apply one's result to the other's, yielding a new action (e.g. Just (*5) <*> Just 2 = Just 10). This introduces a notion of sequence, but does not allow the result of one action to influence what the next will be.

Monad, despite its simple definition (bind and return), can do everything an applicative functor can do and more. Namely, monads allow the result of one action to determine the next (e.g. getLine >>= \x -> if null x then return () else print (read x * 2)).

As for an introductory language for functional programming, I can certainly vouch for Haskell. Thanks to learning Haskell, I feel like I've gotten better at breaking up programs logically, even in languages like C and assembly.

PS: I didn't say anything about the transition from imperative to functional, and I probably should (to really answer the question), but I need to go to sleep.

Functional programming is mainly a style, just like OO programming. Some languages are created with this style in mind (Haskell, ML, Scheme, etc. for functional; Java, C#, C++, etc. for OO), but the principles can be applied to most languages.

I'd say start with a language your already comfortable with, preferably the "most functional" (eg. if you know C and Python, go with Python since it has first class functions). As you learn about functional programming principles (no mutable state, no side-effects, etc.) try following them in your language of choice.

The best advice I can give is to think in terms of values rather than statements. Imperative programming is all about "which action should I perform next?", whereas functional programming is all about "what do I want this to be?". This manifests in a lot of places, some of which I've mentioned below. The difference can be summed up by the following:

// An imperative mindset performs a series of actions
// Check the value of "foo"
if (foo) {
// Set the value of "bar"
bar = FALSE;
}
else {
// Set the value of "bar"
bar = TRUE;
}
// A functional mindset asks what value "bar" should have
// "bar" is the opposite of "foo"
bar = !foo;

Here are a few ways to become more "value-oriented":

Static Single Assignment: define new variables instead of mutating existing ones. Mutating variables is a symptom of imperative thinking ("then do this"), whereas defining a load of constant values is a symptom of value thinking ("I'll need this as well").

Pretend you're using lazy evaluation: if your code is defining a value, it will work under any evaluation strategy (assuming it halts). If your code is highly imperative, it will break under lazy evaluation (since it's effectively 'executed backwards').

Separate functions from procedures: try to make each function you write either calculate a value (ie. a pure function) OR perform some side-effect (ie. a procedure) but not both. This way all of your calculations can focus on their result value, rather than worrying about interleaving of effects.

Use ternary expressions: in imperative languages, if/then/else is used to send execution down different paths, which is a very non-functional thing to do. Instead of choosing different actions to perform, try using ternary operators to choose different values to assign. I'd recommend using extra variables rather than nesting them though, since they can get difficult to understand (just like the branching logic of an imperative program).

Use recursion instead of loops: be careful, as you might overflow your stack! Then you'll appreciate tail-call optimisation. Many languages have built-in "map", "reduce" (AKA "fold"), etc. which don't have this problem. If not, you can define these yourself using loops then never have to write another loop again.

Group related values together: for example, an if statement which sets different values for "x", "y" and "z" could be turned into a ternary statement which chooses between dictionaries containing keys "x", "y" and "z".

Use comprehensions: these are inherently functional, since they're a mixture of maps and filters, and are a quick way to get into a functional way of thinking. For example:

(In Python):

results = [foo(x) for x in bar if baz(x)]

Use values to represent effects: instead of calling procedures deep within your application, you can instead make a note of the procedure you want to call and put those notes into your return value. For example, instead of calling "foo" with "x", "y" and "z" you could return a list "[("foo", x), ("foo", y), ("foo", z)]}" and get the caller to run them for you. Applying this same principle, the caller can instead put them into its return value and pass them up to its caller, and so on. This way, lots of your procedures become pure functions: they're calculating a list of procedures to call, rather than calling procedures directly. This may sound silly at first, but it's basically an inverse of dependency-injection. It's a bit more boilerplate, but it offers some nice advantages: behavioural tests don't need mock objects (just look at the procedure-list), more code is unit-testable since it doesn't cause effects, procedures can be ignored/rearranged/decorated/retried, etc. Note that this is a form of message queue, and also an instance of the "command pattern".

Factor out pattern in your procedure-lists: "I don't care about the results, just run them in this sequence" is exactly what functional programmers call a Functor; "Chain these together so the result of the nth procedure is sent into the (n+1)th procedure" is exactly what functional programmers call an Applicative Functor; "The nth procedure could be this or that, depending on what the (n-1)th precedure returns" is exactly what functional programmers call a Monad.

In my opinion, these are good programming practices anyway. Once you're used to them, you'll find that languages desgined for functional programming (eg. Haskell) make these practices much easier (eg. they have tail-call optimisation, they make mutable variables explicit, they have very powerful pattern matching and comprehensions, etc.)

The best experience I had as a UNIX developer learning lisp was learning how to write emacs extensions using emacs lisp (elisp). The ability to have your text editor do anything you want is a powerful learning incentive, and you don't have to write much lisp code to get emacs to do something useful.

When you learn a different paradigm you learn a different way to approach the problem. The challenge there is retraining yourself to not immediately go down the path of a procedural or OO solution. For the most part I think most of your challenges will be of that nature. The concepts and applications are pretty straightforward, getting used to it was the hard part for me.