back
19 comments
No one is reinventing functional programming, as another commenter said, purity is just one aspect of functional programming and unit testing is used heavily in functional programming projects. In languages like Haskell where the type system can do most of the heavy lifting for you (ala QuickCheck - Erlang has a version of it too if you use spec definitions piously) unit testing is less repetitive and much easier to maintain.

What unit testing is in languages that don't have a stronger type system is simply working around warts in language design. Just as ORM systems are a "work around" for the warts in interfacing relational data with an object model.

[EDIT] I didn't write out my thought well enough: unit testing in "other languages" isn't necessarily working around the warts in language design but working around the warts in programmer thinking and process. Strongly typed languages don't solve those problems but they do improve the situation dramatically with their type guarantees and advanced testing systems (QuickCheck) therefore reducing the amount of cognitive overhead and code maintenance involved in writing tests. With languages like Haskell you only write Unit Tests for things that are difficult or complex to test using QuickCheck.

Can functional programming languages guarantee software regression never happens? No? Then I'd argue they still need unit testing.

Side-effects create additional problems, but functional programming doesn't solve all problems inherent in programming.

One point in support of this point of view is that with functional languages such as erlang and haskell you can generate unit tests using something like quickcheck.

http://en.wikipedia.org/wiki/QuickCheck

Re-implementations of QuickCheck exist for C,[2] C++,[3] Chicken Scheme,[4] Clojure,[5] Common Lisp,[6] D,[7] Erlang,[8][9] F#,[10] Factor,[11] Io,[12] Java,[13][14][15] JavaScript,[16] Node.js,[17] Objective-C,[18] OCaml,[19] Perl,[20] Python,[21] Ruby,[22] Scala,[23] Scheme,[24] Smalltalk[25] and Standard ML.[26]
The problem is that most of those implementations--even the OCaml one--are much more awkward to use than the Haskell version. So people don't.

In Haskell, the type system--thanks to typeclasses--can figure out how to generate random inputs automatically, just based on what type is expected. Even if you have a custom type, it's incredibly easy to define how to generate elements and then use it everywhere. Or you could easily randomly generate values in an ad-hoc method wherever they're used.

This takes advantage of the fact that in Haskell, values can be polymorphic on their return types. As far as I know, this is a feature unique to typeclasses--and very few languages aside from Haskell support those. (I only know of one other: Rust.) This, combine with Haskell's usual popular abstractions like Applicative Functors, makes writing QuickCheck-style properties and new random generators much easier.

Of course, this particular reason has nothing to do with functional programming and everything to do with Haskell's glorious type system. However, functional programming does play a role in QuickCheck's popularity as well.

Basically, QuickCheck-style tests are primarily useful for code that does not have side effects. QuickCheck lets you check some invariant by evaluating the same function hundreds or thousands of times with randomly generated input. This style simply does not scale to non-trivial non-local state, much less actual IO.

So even if your language had a usable QuickCheck library, it would only be useful for the more functional parts of your code. As the article points out, unit tests push you towards a functional style to some extent. QuickCheck does this far more. And so most people writing rampantly imperative code in wantonly imperative languages simply don't use QuickCheck. Even if some intrepid soul has ported it.

> In Haskell, the type system--thanks to typeclasses--can figure out how to generate random inputs automatically, just based on what type is expected.

No. The reason why the type system is able to generate these values is because of algebraic data types, not type classes.

Type classes are completely unrelated to this problem.

What?

The way the Arbitrary class works is only possible thanks to typeclasses. Without typeclasses, you would have to explicitly specify which generator to use. Thanks to the typeclass system, Haskell can just infer this.

On the other hand, none of the Arbitrary machinery is particularly tied to algebraic data types per se. It would work as well if you had OO style constructors instead.

Just take a look at the OCaml version of QuickCheck to see what I mean. OCaml obviously has algebraic data types, but it's still far more awkward to use QuickCheck there.

Another limitation of quickcheck that's often overlooked is that simply using quickcheck for testing your function does not guarantee complete coverage of your function. The coverage may also differ between runs. So while it's good for doing a "quick check" of your function it's not a replacement for unit testing.
There are equivalent testing frameworks for non-functional languages.
I've done plenty of unit tests and I've read up on QuickCheck.

QuickCheck is NO SUBSTITUTE for real unit testing. All it does is automatically let you randomize inputs since it can know the input type. Unit tests shouldn't even have randomized inputs, they should be deterministic.

I wish!

Unit tests that verify the in-order invocation of several different void methods have nothing to do with reinvention of purity.

I think a better argument would be: the best way to write testable units is to write pure functions. This argument was made for example in Hughes's Why Functional Programming Matters. But it's not really a question of OO vs FP.

There's a seemingly important pattern that I don't know if there's a common name for. It's something like "Replace Side-Effect With Value." Or maybe "Inversion of Side-Effecting." This is relevant even in Haskell. Either you write your whole program as a big messy monad transformer stack with IO at the top -- I don't know how to unit test that, much less prove anything -- or you define the application logic as pure functions that give instructions to a simple driving "side-effect interpreter."

XMonad, the Haskell window manager, is written in this latter way, with lots of unit tests too. There's a cool video walkthrough of how the code works -- http://www.youtube.com/watch?v=63MpfyZUcrU -- and a document called Guided tour of the xmonad source -- http://www.haskell.org/haskellwiki/Xmonad/Guided_tour_of_the....

Meh. Among the other reasons for testing the author didn't touch on:

* It's not always about the tests. I TDD code (functional as well as OO - hell procedural come to that) as a design tool - not a testing tool. I write acceptance tests as tools to help us understand when we reach "done" and as ways to track progress.

* It's about stopping regressions. Test suites get in the way of accidental behaviour changes.

* There a way to scale team communication of how the code should behave.

I love FP dearly, but if it replaced testing why do I see so much FP code with test suites ;-)

What the author describes seems to be just modularity and encapsulation. The properties most associated with FP, like first class functions, don't appear to be related to unit tests.
There's nothing wrong with applying part of good practice from other languages. Referential transparency is one property of FP, but FP does not hold exclusive right to it. Quit trying to sound like smug.
I am not aware of any non-FP languages that track referential transparency in the type system.
This is simply 'good' programming.
As is becoming increasingly common, people are conflating "functional programming" with "immutable data types" or "side-effect-free functions", neither of which are core concepts common to all "functional programming languages." It's getting quite tiresome.
Can we add [2009] to the title?
It has been very zombierific on HN the last few days.