Hacker News new | past | comments | ask | show | jobs | submit login
Writing unit tests is reinventing functional programming in non-functional languages (erlang.org)
93 points by jamongkad on Feb 25, 2009 | hide | past | favorite | 46 comments



I'd say that it's true that the closer you get to pure functional programming (no state or side effects), the more unit testable your code becomes.

I'd have to take issue, however, with the author's implication that Java programmers who unit tests are idiots who don't realize they're reinventing the wheel. Good developers in any language recognize the importance of avoiding side effects, isolating parts of the system from each other (including minimizing state), etc. It shouldn't come as a surprise to anyone that testable code has some common characteristics across all programming languages. There are differences unique to the language as well, though, and unit testing in Java also tends to involve use of things like interfaces and polymorphism that are distinctly OO concepts as well.

And of course, unit testing isn't just for those "overly complicated" Java applications; it's for any type of system in any language. The author seems to me to imply that somehow unit testing is uniquely a requirement of Java systems that Erlang systems somehow don't need.


The main point the guy makes is that "testable code" resembles functional programming. And testable code is code that doesn't suck. Maybe code that doesn't suck is code that resembles functional programming.

I guess you could argue that all the moronic OO voodoo theory books are trying to say this. I don't think so. I think he's a got a point.


I agree that he's got a point, I just get annoyed when people feel the need to make their point in a way that questions the intelligence of other people. It's unnecessary and counter-productive. There are ways to say "one big advantage of functional programming is that it leads to more testable code" without also saying "and anyone that programs in any other sort of language is clearly an idiot."


It does not question other peoples intelligence, but rather their knowledge of programming language paradigms.

You can program functionally in any language by eschewing side-effects whenever you can. Only that in some languages it is way easier to program functionally than others (ML, Scheme, Common Lisp, Erlang) and in some it is even forced (Haskell). The "idiot" here is the one that can't see or understand that it is easier to program functionally in functional languages and that it leads to better programs by doing so. To see this, you need knowledge of functional programming. And sadly, many people do not understand its finer points because they have not spent time writing non-trivial programs in functional languages.

If you know nothing about FP on the other hand, you could be lead into the concept of Dependency Injection, Design Patterns and whatnot, because your world is shaped around a statical subtyping system (In the case of Java). These concepts are not bad at all: they help Java programmers write better code (for most part) with fewer errors.


I also agree that he's got a point. I'd encourage him to build something so awesome with a functional language that the rest of us can't help but be blown away. Ejabberd is a good start, how about some more?

It's not easy to develop things so useful & elegant that you win people over... but it's easier than arguing the Internet into submission from a mailing list.


Actually, there are two views of functional programming:

1) Default to functional ideas: Variables are immutable, recursion is encouraged, etc; but you can get access to imperative code when needed (Call this the Algoritmically functional languages)

2) Functional is the norm: You can't write non-pure functions with side-effects at all. These are the Pure Functional languages.

I am leaning towards 1 more than 2 personally, but many people would disagree with me on that one. The question is: Do you want a usable program written in class 1 or 2? Because there is a hell of a difference between them and how you build software for the two classes.

Interesting class 1 projects, where you can get the source code, are: ejabberd, mldonkey, unison, harmony/boomerang, coq, twelf, isabelle, coccinelle, and mlton.

Interesting class 2 projects could be xmonad, darcs, and GHC.

Of these projects, some would be easily implementible in imperative languages, some would be hard but doable and some would be insanely impossible to do. Please note that all things on the list are useful and elegant. But they might not have a user-base as big as other projects.


A shame no one has replied to Jim with a strong app written in the functional language.

The closest thing I know is the website which became yahoo stores. It was originally written in lisp (no really functional, I know). The lisp worked great when they had a dedicated lisp manager managing it. When Yahoo took over, the rewrote it in something else, not because the something-else worked better but because they couldn't find/manage the development in lisp.

So functional might be failing this way. But it might just be hard to write big functional programming applications. I'd like to here more about this.


"An Algorithm for Compressing Space and Time" -- amazing performance improvements for "Game of life". (I can't find an old submition so here's a new one http://news.ycombinator.com/item?id=495438 )

Hashlife algorithm might find some benefits from implementing it in a functional language.


> I'd encourage him to build something so awesome with a functional language that the rest of us can't help but be blown away.

Functional language helps a lot in building of side effect free code. But such code can be written even in pure assembler. I writing in such style in any language, including Bash.

Sometimes I need to refactor somebody else code. Code functionality remains exact same after refactoring. Things that changed are: better code clarity, better reusability, and better testability as part of reusability.

Unit tests creates additional contexts, in which main code can be used, thus they force to improve reusability of the code, thus code forced to make less or none side effects, thus programmer forced to rewrite code in functional style.


How do you write such code in x86 assembler? All the instructions I'm familiar with have side effects except for nop, hlt, and, arguably, push and call.


It is not a problem. Execution of any code in any language can have side effects, like garbage in memory or stack overflow or OutOfMemoryException. Usually, you just ignore these side effects.

Use code convention to define which flags, registers, and memory areas, IO registers, etc. must be preserved by the called function. Just ignore any other changes.


I agree that avoiding side effects and global state is a well-known practice (and OO languages have access modifiers, like private, to support it). It's not unique to functional programming.

Interesting to me was that the reasoning of the article was strikingly close to Dijkstra's "Goto considered harmful", and it makes me wonder if there is some small step that OO languages could take to automatically enforce testable code, in the way that structured programming made gotos safer.

The problem is that side-effects and global state are very useful for making some problems simpler - which is why Erlang has its transactional database mnesia, and Lisp is not purely functional.

Is there a way to structure side-effect/global state to make it entirely testable, without destroying the benefits?


That's also generally the reasoning behind mocks, dependency injection, etc. used by OO unit testing. If something is supposed to, say, update something in the database, you abstract out an interface to the database and mock it out in your tests, and then use interaction tests to verify that your code is calling the right methods on your database abstraction.

Of course, it's kind of turtles all the way down, and you then have to test that your production implementation of that database abstraction does, in fact, call through to the database. And of course, there are the ever-raging holy wars in the tdd community around interaction testing versus state testing, and that level of abstraction and mockage can sometimes (in my opinion) make the code harder to follow, so it's always about tradeoffs.

But whatever the language you're in, encapsulating side effects and state as much as possible is going to be the way forward, using whatever tools are at your disposal. Some languages tend to make it easier to do that (or harder to avoid doing that) than others.


I can't see an argument that Erlang is automatically tested in the body of the link. Perhaps I missed something. All I read was the usual arguments for functional programming - which has some merits but ... whatever. Where is the unit testing connection?


Starting from the 5th last paragraph:

The funny thing is that the OOP world have found one way to manage the complexity and the code-bases that grow ugly: They are using unit tests, and practice the art of writing testable code. ("Testable code" is something that is simple to write unit tests for. )


Monads are one attempt at solving this problem.


This actually is what drew me to functional programming in the first place. I'd recently been on a unit testing kick, and figured that if you get rid of mutable state and have function outputs depend only upon inputs, your code suddenly becomes much easier to unit test.


[deleted]


Probably, yes. They get a bit less useful, but they also get a bit easier to write.

And the point of unit tests isn't really to smoke out every possible problem: it's to give you confidence in your code, so you can say "This will work" instead of "This should work". Functional programs still need some sort of test suite to achieve this confidence.


Referential integrity in code-under-test is convenient for unit testing, so unit tests encourage (re)discovery of functional style by programmers who are working in non-functional languages.

But unit tests also have value in programs that are written in functional languages and in a functional style. Unit tests reinforce essential properties of the program and thereby formalize the accidental/essential boundary. They can provide guidance about how API elements are intended to be used together. They can present elements and properties of a program in an order that is best for explanation (they can be a crude form of literate programming). So, it's wrong to reduce unit testing to functional programming.


Look up the technique of "dependency injection" sometime. This is all the rage among Java consultants, their favorite technique to make things testable. It's an elaborately disguised way of saying "let's rewrite this function so it obtains all its state from the caller". In other words, functional programming.

In cases where passing the same list of initialized objects up and down the call stack becomes tedious, Google's Java people have invented frameworks like Guice. This makes dependency injection appear to happen by magic.


Dependency Injection allows to assemble program on the fly. It allows to assemble programs written in different styles, including functional.

Look at Spring Dependency Injection Framework for example. It supports lot of libraries, which are written in traditional style.


I don't mean to be snarky, but that sounds a lot like passing a closure and maybe some other state in.

I don't really agree with the OP's point in any case. Even if I have an entirely purely functional library, I still need to unit test it to make sure it does what I think it will and that it observes the edge cases I know of correctly and further so that I can regress it if I need to make changes, maybe for performance reasons at some point.

I mean, it is nice to reduce state in your code as much as possible, but its not always practical or efficient. I think it's great that people are getting more excited about Functional Programming because I believe that there are a lot of great techniques that become available when you understand it, but all that imperative knowledge is still going to have its place at the end of the day. After all, writing an in-place quicksort is always going to end up involving imperative directions to the computer!


Maybe you should try reading a bit about dependency injection. It is a declarative way to assembly pieces of software (in this case, objects) that have no knowledge of each other. You can do it manually, but this is an easier way to do it.


Dependency injection is great for testability and awful for code clarity as far as I can see.

I started using dependency injection as f(x,y, z) where x and y were generally the same.

I changed the reference in my code to y.x = x y.f(z) and it got so much clearer and dryer despite losing the dependency injection. Perhaps I'm doing this wrong but dependency injection seems like cruft from the test department. I am not an OO fanatic but OO is extremely useful for juggling a bunch of information in the best bad way possible. And juggling information in that best bad way possible is a way different problem from functional programming paradigm of finding perfect, elegant solutions to well defined problems.


I am not an OO fanatic but OO is extremely useful for juggling a bunch of information in the best bad way possible. And juggling information in that best bad way possible is a way different problem from functional programming paradigm of finding perfect, elegant solutions to well defined problems.

What's the difference between:

    data Foo = Foo { a :: String, b :: String }
    doSomething :: Foo -> String
    doSomething foo = a foo ++ b foo
and

   class Foo {
       String a;
       String b;

       String doSomething(){
           return self.a ++ self.b;
       }
   }
If there's some deep fundamental difference between functional programming and OOP, it's not this.


So... I am an avid TDD/Unit Test/BDD guy. I've been gravitating towards BDD lately and honestly I don't DI everything. Really if I'm just BDD-ing a layer I just need to DI the fringe dependencies, the things that would cause me to be dependent on other whole systems or sub-systems (think File I/O, Active Directory, Web Services, etc). My object still contains a lot of state.

I think saying that unit tests cause us to reinvent FP is a bit of a stretch. I do think that saying that unit testing causes us to write better code and we borrow heavily from many programming paradigms (DBC, FP, OOP, DSLs, etc) in order to write that better code.


I think you have overdosed on acronyms.


So... I am an avid Test Driven Development/Unit Test/Behavior Driven Development guy. I've been gravitating towards Behavior Driven Development lately and honestly I don't Dependency Inject everything. Really if I'm just Behavior Driven Develop-ing a layer I just need to inject the fringe dependencies, the things that would cause me to be dependent on other whole systems or sub-systems (think File Input/Output, Active Directory, Web Services, etcetera). My object still contains a lot of state.

I think saying that unit tests cause us to reinvent functional programming is a bit of a stretch. I do think that saying that unit testing causes us to write better code and we borrow heavily from many programming paradigms (Design By Contract, Functional Programming, Object Oriented Programming, Domain Specific Languages, etcetera) in order to write that better code.

:)


Interesting point, although I would argue that writing unit tests is more like reinventing Design by Contract.


Not really, contracts can't send in invalid data to see what happens and ensure the proper actions are taken.

Contracts don't prevent the need for tests, they simply augment them. Contracts only state assumptions about how code should behave, but they don't make sure those assumptions are correct, only tests can do that.


Upvoted you after some baffoon voted you down for no apparent reason.

Seems like downvoting because of disagreement is getting more and more popular these days.


I really like functional languages while others hate it, but I don't think functional languages will take off by itself. The reason why functional languages don't work is that they're they wrong metaphor. Object oriented programming works because it fits the user model of how to describe actions and the world.

Features of functional languages will influence PL (Ruby, EMCA script, Python), server paradigms (map reduce), but I don't think it'll ever go mainstream by itself.


Better put 'functional programming is reinventing the way unit tests are written'. Its extremely easier to 'think of' and create unit tests on business logic that avoids using state and mutable data, as do all functional languages.

On the other hand, this is pretty much offset because structuring a program ( the one on which the tests will be performed) using a pure functional style is very hard, especially when its syntax/compiler doesn't enforce it.

Languages like Java which have fields, getter/setter methods and similar syntax are mine-fields for introducing state and mutable data. Erlang really forces you to think in functional style given its 'message passing' style, so unit tests are a snap. In Java and other non-functional languages unit tests would be just as easy, the problem is avoiding state and mutable data.

Though I would still argue the effort forgone in unit testing, still needs to be invested designing the actual business logic taking care it doesn't introduce state and mutable data(non-functional behavior).


Loads of "modern" stuff - unit testing, design patterns, continuous integration, aspect-oriented programming, etc - is people trying to retrofit FP ideas back onto OO code.

This is far, far more work than just using a functional language in the first place!


This will sound like a trollish comment, but can anyone point to a modern web-app written in a purely functional language?

I'm curious how several design problems, such as DB access and authentication, have been handled...


There are some things linked from http://www.haskell.org/haskellwiki/Practical_web_programming... but I don't know if they're Modern. Maybe they're Postmodern or Futurist or something.


Or maybe they just seem rather, uh, simple. If you are saying you this is a serious programming language, shouldn't you be able to show several large, complex and commonly used application - by now?

I, too, studied fp in grad school, fifteen years ago now. "Where's the beef" can seem like a cheap but if you have ... beef, it should be easily answered.


I appreciate side effect programming, but I really think the java criticism is unwarranted. The first thing I think of is: "How do you do a CRUD app in your functional language?"

I enjoyed ML when we were doing it in school. However, I've only worked on one application that could be translated easily to the functional paradigm. The rest have parts that would be well suited to functional programming, but the rest of the program is one giant side effect. Monads aren't the answer there, they're a workaround.


A LOT of "ordinary programming" is moving some messy bits of information from one place to another, taking into account some messy cases and occasionally putting information into a third, messy place.

This kind of programming has little mathematical content and little similarity to the canonical factorial example that every functional programming language LOVES to give.

Here, functional programming seems to lose its elegant minimalism. Indeed, when you have a bunch of arbitrary, (meaning random) cases and conditions, no language can make your code elegant and minimal since your code must contain the information in the cases and random information is incompressible.

I mean, how would you write a WIN32 GUI in a functional language?

Show me otherwise but I haven't seen the good argument for functional programming languages as general purpose languages and so the folks dumping Java don't really seem very much better.


I mean, how would you write a WIN32 GUI in a functional language?

Just like you would in any other language?

Functional programming is not different from any other type of programming. It makes some things easier, but other things are about the same.

I think a GUI app would be easier in, say, Haskell, than Java, for a number of reasons. The lack of global state will make threading easy, which keeps the GUI responsive. Abstractions like functional reactive programming will make the app's implementation cleaner. Finally, if you do it right, most of your app won't depend on the GUI, and will be very easy to test. (You can do that last one in any language, of course.)

Assuming your application does something useful, implementing the algorithmically complex part will be simpler as well.

Anyway, you can't understand the advantages of functional programming from reading some comments on a social news site. You actually need to try writing the application -- then you can see exactly what the benefits are, and whether or not you care about them. If you don't want to invest any effort in learning, well, that's another issue entirely. (And is really the reason why many people don't all switch to Haskell. Learning something new is scary!)


I mean, how would you write a WIN32 GUI in a functional language?

F# seems like a good choice.


How do you do a CRUD app in your functional language?

I'm not going to give you a long tutorial on this, but it's not hard. You write it like you would anything else.

You have some data that you want to manipulate. You define some way to store, edit, and retrieve that data. (Libraries can handle this for you.)

Then, you make the web UI like you would in any other language. At the top is a function that takes a request and returns a response, which is entirely pure. Then you implement the helper functions as needed; Map URIs to handlers, map request parameters to data to interact with, interact with the data, generate a response, etc.

Anyway, it is not that different from anything else. Why would it be?

the rest of the program is one giant side effect. Monads aren't the answer there, they're a workaround.

You are thinking about the problem wrong.


There is no reason that side effects have to be difficult to debug. You can think of the lookup chain for a variable as being part of a data structure being passed in to a function. It is just as easy to design a convoluted data structure that gets passed in to a function as it is to design a convoluted object hierarchy.

The difference is that you can't get implicit lookups for free in an fp language unless you implement something like messageSend(name, obj, args).


This is unrelated to the post, but I noticed that instead of removing the email addresses from the thread, they've replaced them with images that are using a vary standard looking monospace font. I would think that any program that mentions "OCR" (including the 20 minute template matching hack you could do in Python with PIL), would be able to harvest addresses from it...


I'll have to tell those haskell guys to throw quickcheck away then, since they don't need unit testing...


Did you just read the headline? Maybe a better phrasing would be "The need for unit testing is causing OOP guys to write in a functional programming style."




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: