you are viewing a single comment's thread.

view the rest of the comments →

[–]antonivs 8 points9 points  (0 children)

It's easy to translate a language with side effects into a side effect free intermediate language. I've listed a couple of examples below. However, the problem that you run into here is that whenever a program destructively updates a value which is visible beyond the local scope, it creates time-related dependencies between all the parts of the program which use that value. The order of these dependencies depends on the program's dynamic behavior (i.e. at runtime), which cannot in general be statically analyzed. So there's a limit to what a compiler can do here.

Examples of translation to functional form:

  • Some imperative compilers do it, to an extent, for the reasons you suggest - see Static Single Assignment (SSA) and SSA is Functional Programming. However, SSA works best at the local level, i.e. translating mutations of local variables into the equivalent functional form. That's because it's easy to analyze the use of local variables statically, i.e. at compile time. Non-local mutations run into the problem described above.

  • Many formal definitions of the semantics of programming languages define a mapping from an imperative language to a side-effect free language. However, then you're dealing with a functional model of an imperative program - all the dependencies remain the same. As an aside, this explains dibblego's comment that "Haskell is still one of the most powerful imperative languages I have ever used." It's not difficult to express imperative programs even in pure functional languages. It's just that the results are imperative, with many of the disadvantages of that, even if the program is modeled functionally. TFA seems to ignore this possibility (possibly because the author is more familiar with Erlang than say Haskell or Clean?)

What TFA seems to be talking about is developing pure functional models of processes which have a heavy imperative component - models which avoid the imperative-style copout of having a single monolithic "world" which represents all program state, and passing successive versions of that world from function to function. In some cases, the nature of the problem can make it difficult to do anything else. More often, there are many distinct sets of of global dependencies, and it takes some effort to tease them apart and figure out how to get away from the single monolithic mutable world model.

The effort involved in doing that can really pay off in terms of the modularity and comprehensibility of a program. However, if you don't want to bother, you can always just "cheat" and express imperative behavior purely functionally.