Most of the troubles with functional programming come when you actually have to do something. The imperative/functional boundary remains a problem.
How does it remain a problem? I've been writing commercial Haskell programs that do something for years now.
[1] http://research.microsoft.com/en-us/um/people/simonpj/papers...
That some pure languages like Haskell decided to treat mutation as a side-effect like IO (requiring monads) only exacerbates matters.
Nah, when you say "do something", you are referring to something we call "side effects" and is generally frowned upon.
Your program should consist of pure functions only, be side-effects free, and there should be no state.
Of course, such a program can't do anything useful, but you see, that is not the point.
And if you don't see the point, you can't be helped.
Am I doing this right?
> Doing a functional language and combining it with the ability to do real things is very hard. And everybody has had a problem with it.[0]
Or, as the editors nonsensically summarize it:
> FP [the language] is not suited to many mundane programming tasks, particularly those that consist primarily of fetching data, updating it, and returning it to some database. Tasks such as updating an account balance are naturally suited to the von Neumann paradigm.
This book is for a general audience, but that's a particularly bad example since ledger accounts are just about the oldest form of append-only database. (edit, Like old enough that Chaucer makes a very raunchy joke about "double entry" bookkeeping.)
[0] From chapter 1 of _Out of Their Minds_, by Shasha and Lazere.
In an imperative language (including all functional ones), optimizing a program is rewriting it. In a declarative language, optimizing a program is adjusting the language implementation (e.g., making a smarter SQL query planner).
A better argument for declarative languages is the Haskell technique of decoupling the abstract syntax of a DSL (usually a free monad) from its possible interpreters (monad morphisms).
In other words, it's not a lie to promise that you can specify a query over tables in a declarative way, but it's sort of a lie to promise that you can spell out predicates describing a solution and the machine will find a solution satisfying these predicates, because sometimes it can't. Specifying an execution strategy doesn't make it less of a lie but it lets you write a working program kinda looking as if it weren't a lie.
Yup! But it's not as bad as it seems. In a pure logic language, the changing the execution strategy can only change three things:
(0) Whether a query terminates, in which case it returns the entire soution set.
(1) If the query doesn't terminate, which subset of the solution set is returned.
(2) How fast it runs, of course.
Which is a lot better than the subtle things that can go wrong when optimizing imperative programs by hand. Selecting an execution strategy for a declarative program is no different from tuning a garbage collector: if you do it wrong, bad things can still happen, but they won't be as bad as if you manage things by hand and do it wrong.
> it's sort of a lie to promise that you can spell out predicates describing a solution and the machine will find a solution satisfying these predicates, because sometimes it can't.
Yup! Which leads (IMO) to the conclusion that general-purpose languages must always include some imperative (explicit control) component. Declarative languages really shine as DSLs, though.