I'm interested in better learning functional programming. To do so, it seems obvious that I should force myself to use the purest possible functional programming language. Hence, I'm here asking, more or less, for an ordering of functional programming languages according to their purity.

It seems to me that it would be more practical to learn Lisp or Clojure (or Scheme, or Scala, etc.), but for what I've heard recently, Haskell would be very hard to beat at teaching functional programming principles to someone. I'm not sure about this yet, so I'm asking you: which is the purest functional programming language? An ordering would be great if several are competing for the magnificent title of the purest functional programming language.

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise.
If this question can be reworded to fit the rules in the help center, please edit the question.

1

I learnt Miranda at university so I'm biassed, but I would suggest Haskel to anyone wanting to immerse themselves in a functional language without the distractions of impurity. *8')
–
Mark BoothFeb 6 '12 at 15:59

After you're done learning functional programming, you should also learn statically typed programming with an expressive type system. In the combined category (both functional and typed), I suggest: Coq > Haskell > OCaml > Scala > others. There are some less popular alternatives that fit in between Coq and Haskell (like Epigram and Agda). Haskell misses OCaml's expressive module system.
–
lukstafiJul 5 '13 at 22:01

Not quite: Haskell does allow side effects (IO monad). It's just that side-effect-causing code is clearly marked as such. I don't think it's useful to talk of pure/impure languages at all (Haskell lets you program imperatively! Gasp!) but it can still be useful to talk of chunks of code (function, module, program, whatever) as being pure/impure.
–
Frank SheararDec 10 '10 at 9:48

6

@Frank: Yes, Haskell does allow side-effects, but it does more than just marking side-effect-causing code. It also maintains referential transparency even when using side-effect-causing code and that's what it makes pure - at least by many people's definition of purity. Of course that doesn't match missingfaktor's definition of "no sideeffects allowed".
–
sepp2kDec 10 '10 at 13:08

3

@Frank Shearar but it is useful to talk in a such a way because the IO monad is pure. Its the runtime library that isn't, not your code, since your main function is basically a huge state transformer. (Something like main :: World -> World behind the scenes)
–
alternativeApr 20 '11 at 18:20

1

My language has referential transparency, too. You just write program = "some C code", and then the runtime environment deals with the C code. :-)
–
DanielJun 14 '11 at 23:12

2

As printing out to the screen is a side-effect truly pure functional programs are pretty boring.
–
user1249Nov 22 '11 at 11:01

I personally categorise languages in three tiers of functional purity:

Pure functional languages - i.e. those that treat your entire program as a pure function and handle mutability solely through interaction with the runtime - Haskell is probably the canonical example

Impure functional languages - i.e. those that emphasise a functional style but allow side effects. Clojure is clearly in this category (it allows mutation in a controlled fashion as part of its STM framework), also OCaml or F#

Multi-paradigm languages - these are not functional languages first and foremost but can support a functional style by the use of first class functions etc. Scala is a good example here, I'd also put Common Lisp in this category and you could even include languages like JavaScript.

In your situation, I'd suggest learning Haskell first, then Clojure. This was what I did and it worked very well for me! Haskell is beautiful and teaches you the purest functional principles, Clojure is much more pragmatic and helps you get a lot done while still being very much functional at heart.

I don't really count the third category as functional languages (although after learning Haskell and Clojure I often find myself taking advantage of functional techniques when using them!)

Within very strict limitations, even C has functional capabilities. (The main limitation is on runtime synthesis of functions, which is truly horrendous in C, so much so that most people claim it can't be done.)
–
Donal FellowsJun 15 '11 at 8:40

1

@Donal Fellows: In the limit as silliness goes to infinity, every Turing-complete language is functional: one need merely implement a Lisp interpreter in the language, and then use that. :]
–
C. A. McCannJun 16 '11 at 0:55

Paradigms are meaningless if you take the viewpoint that everything is Turing complete and therefore can emulate everything else. When you think about paradigms you have to focus on what the language makes idiomatic and what it discourages/prevents. To count as a functional language in my view, mutation and side effects must be unidiomatic (for impure FP) or prohibited (for pure FP). That means C is not functional. Neither are multi-paradigm languages like Scala.
–
mikeraNov 22 '11 at 10:56

@mikera: I find your answer very interesting: if Scala is not functional but only multi-paradigm, how do you judge functional programming features in C++11, C#, or even Java (the planned introduction of lambdas)?
–
GiorgioMar 10 '12 at 20:20

@Giorgio : I think functional features in all the languages you mention are probably a good idea, they just don't make them "functional languages". Or to look at it from the opposite direction, the fact you can do imperative-style monadic IO doesn't make Haskell an imperative language :-). Multi-paradigm languages are basically the result when you bring features from lots of different paradigms together and don't put any particular style first and foremost. IMHO C++, C# and Java are all all moving towards being multi-paradigm, but aren't quite there yet since class-based OOP is still dominant.
–
mikeraMar 11 '12 at 13:51

Impure languages don't really differ in principle from the more familiar imperative languages, especially now that many functional tricks have been copied. What's different is the style - how you solve problems.

Whether you count Haskell as pure, or count the IO monad as impurity, Haskell style is an extreme form of this style and well worth learning.

The Haskell IO monad is derived from the mathematical theory of (of course) monads. However, for imperative programmers, I think a backwards way of arriving at monads makes more sense.

Phase one - a pure functional language can easily return a big string value as its result. This big string can be the source code of an imperative program, derived in a pure functional way from some requirement-specifying parameters. You can then build a "higher level" compiler that runs your code generator, then automatically feeds that generated code into the imperative language compiler.

Phase two - rather than generating textual source code, you generate a strongly-typed abstract syntax tree. Your imperative-language compiler is absorbed into your "higher level" compiler, and accepts the AST directly as source code. This is a lot closer to what Haskell does.

This is still awkward, though. For example you have two distinct kinds of functions - those evaluated during the code-generating phase and those executed when the generated program is run. It's a bit like the distinction between functions and templates in C++.

So, for phase 3, make the two the same - the same function with the same syntax may be partially evaluated during the "code generation", or fully evaluated, or not evaluated at all. Further, discard all looping construct AST nodes in favor of recursion. In fact, discard the idea of AST nodes as a special kind of data altogether - don't have "literal value" AST nodes, just have values etc.

This is pretty much what the IO monad does - the bind operator is a way of composing "actions" to form programs. It's nothing special - just a function. Many expressions and functions can be evaluated during the "code generation", but those that depend on I/O side-effects must have evaluation delayed until run-time - not by any special rule, but as a natural consequence of the data dependencies in expressions.

Monads in general are just generalisation - they have the same interface, but implement the abstract operations differently, so instead of evaluating to a description of imperative code they evaluate to something else instead. Having the same interface means there are some things you can do to monads without caring which monad, which turns out to be useful.

This description will no doubt make purists heads explode, but to me it explains some of the real reasons why Haskell is interesting. It blurs the boundary between programming and metaprogramming, and uses the tools of functional programming to reinvent imperative programming without needing special syntax.

A criticism I have of C++ templates is that they are a kind of broken pure functional sublanguage in an imperative language - to evaluate the same basic function at compile-time rather than run-time you have to re-implement it using a completely different style of coding. In Haskell, while impurity must be labelled as such in its type, the exact same function may be evaluated both in a meta-programming sense and in a run-time non-meta-programming sense in the same program - there is no hard line between programming and metaprogramming.

That said, there are some metaprogramming things that standard Haskell can't do, basically because types (and maybe a few other things) aren't first-class values. There are language variants that try to address this, though.

A lot of things I've said about Haskell can be applied in impure functional languages - and sometimes even imperative languages. Haskell is different because you have no choice but to take this approach - it basically forces you to learn this style of working. You can "write C in ML", but you can't "write C in Haskell" - at least not without learning what's going on under the hood.

If a pure functional language is such, that only has pure functions (routines that don't have side effects), then it's a little pointless, because it cannot read input or write output ;)

Because this is really for learning, I think isolation isn't necessarily the way to go. Functional programming is a paradigm. It is important to understand which paradigms are suitable for which problems and more importantly, how they can best be combined.

I'm going to say it now: programming
fashions are stupid and
counterproductive. The only things
that matter are that your program is
short, easy to write, easy to maintain
and works correctly. How you achieve
this has nothing to do with
programming fads.
- Richard Jones

Other than that, if you're looking for "purity" you might want to have a look at Pure. Note however, the extreme ease of calling C routines makes it functionally unpure (but also very powerful).

Since learning is your goal, and not writing programs per se, you can't get any purer than Lambda Calculus.

Lambda Calculus was around before computers were invented. It took several skilled logicians working on it to figure out how to do subtraction (for a while it was theorized that only addition and multiplication were possible).

Learning how booleans and numbers and if can be invented from seemingly nothing won't put more gas in your tank, but it will make your tank much bigger.

Granted, there's probably no purer than that, but we're crossing the math barrier a little too much. I still want to practice programming and learn a new language while absorbing functional principles. Though, I agree that it might be interesting to study lambda calculus just for the sake of better understanding the foundations of the functional paradigm (and having a bigger tank).
–
M. JoanisDec 10 '10 at 17:03

1

Writing a proof is writing a program and writing a program is writing a proof. When you learn the about the Curry–Howard isomorphism you'll realize you passed that math barrier ten thousand lines ago.
–
MacneilDec 11 '10 at 3:23

OK, now I'm confused. How can you have addition and multiplication but not subtraction? X - Y can be trivially composed from addition and multiplication as X + (Y * -1).
–
Mason WheelerJun 14 '11 at 22:54

@Mason Wheeler: λ-calculus alone doesn't have numbers, or really any sort of data besides lambda abstractions. Unless specified otherwise, anyone talking about numbers in λ-calculus probably means Church's encoding for natural numbers, where a number n is represented by n-fold function composition. That said, I'm dubious it was that much a struggle to figure out subtraction once someone actually tried; I worked it out on my own in an afternoon given only the definition of addition and knowledge that subtraction was possible.
–
C. A. McCannJun 15 '11 at 3:16