Friday, February 3, 2012

Functional programming explained

I've noticed a lot of confusion over why functional programming languages seem to be what the cool kids use these days, and why they can actually make a difference in your productivity as a developer, and I hope I can clear that up with this.

Consider a standard, run-of-the-mill imperative language, like Java, or C.

Your procedures or methods typically look like this:




Basically, you're taking two numbers as arguments, adding them, and returning them, and saving the result to a database somewhere in the middle.

Not too shabby, right?

Consider this situation. For some reason (your application isn't working), you need to check if this particular method is working.

What's the problem? Well, you can't test this method separately, because it needs access to the Database class, which needs access to about 6 billion other things!

The ability to test things separately from others is called "decoupling".

Another issue with imperative languages is that they can modify all kinds of state in one function!

So, in effect, when you call the function addNumbers, it could delete your database, empty your bank account and draw a circle on the screen. Why is that a problem, you ask? Well, that's a pain to debug, because you have no idea what types of functions can modify what types of state!

Another issue are global/static variables, which cause no end of problems for Java programmers who find themselves changing static fields in certain objects affecting all the others in adverse ways, and this takes a ton of work to weed out.


How does functional programming solve these problems?

The first thing to understand about functional programming is that in a completely pure functional programming language, there is no way you can modify external state.

That means you can't print to the screen (that's changing the state of the screen), access the web (changing socket state), or anything particularly useful.


But, this wouldn't be terribly useful since you wouldn't be able to get any output shown, so, it would be practically useless (people get around this by writing languages that printing every calculation to the screen as a part of the language implementation, but, this just creates more confusion).

However, that's not to say that they aren't used. An example of an ancient and hugely successful purely functional programming language is mathematics (there's no "print" command in math).

So, languages like Haskell use this special idea (borrowed from mathematics, see my abstract algebra posts) called a Monad to "mark" special functions that do modify state so that they can be separated cleanly from the rest of the program.

This nearly kills off the decoupling problem!

Because there are very few non-pure functions in a typical functional piece of code, everything else IS decoupled from the environment!

This is heaven when you need something debugged!

And, this cleans up what functions can do and can't do; every pure function MUST return a value, so, you can expect something in return, not just a void (because that would mean its changing state, is therefore not a pure function), so, if you try to write to the database from a pure function, you can't do that, which keeps your code squeaky clean.

Usually, functional languages come with malleable and nimble static typing, which means that you don't have to write out the types when you're declaring functions, but, they're there, so, when you try to pass a String to a function that wants an Integer, the compiler will tell you.

All of these restrictions make it much more difficult to compile, but, once it does, it sure as heck won't crash (it might not do what you want it to, but, that's what unit tests are for).

In functional languages, variables aren't really variables in that they can't be changed.

You're probably saying "WHAT?!! How am I supposed to implement a counter then?!", and that's solved entirely by the fact that you can use recursion.


So, that's why functional languages are awesome, and you should go check out Haskell!



17 comments:

  1. Nice post.Why recommend Haskell? Why did you choose Haskell over other functional programming languages like Clojure, Scheme, Lisp, Erlang etc.

    ReplyDelete
    Replies
    1. Thanks for the feedback!

      I chose Haskell because of the following reasons:

      1) I'm familiar with it
      2) Its type system is amazing; static typing with sprinkles
      3) It has a big community

      You could use the same reasoning with Erlang, for the most part.

      Delete
  2. After reading this post I have feeling of a huge mess between functional/procedural/declarative/imperative/pure functions/ and other kind of things.

    In my opinion, functional programming is about higher order functions and closures used as main construction objects. And pure functions have nothing to do with that, you can use pure functions even at procedural programming style.

    And declarative is opposite to imperative, but it's not a synonym to functional at all.

    ReplyDelete
  3. You're conflating decoupling with functional testability. Even though decoupling can help testing, there are ways of isolating the code under test, e.g. mocks.

    Higher-level units (functions, methods) under any language have the same difficulty in functional testing. If you're going to assert the end result of said unit, and it calls lots of other units, you're obligated to do a lot of work, in any language.

    Using mocks allows you to isolate the code under test, but reduces your coverage to the narrow definition of "playing nice with everybody else." In your example above, Database.write would be mocked, and the passed arg asserted to be the expected value.

    This makes several assumptions: (1) Database.write behaves the way you expect; and (2) that its interface won't change. I believe this is fine for unit testing, but it certainly moves you away from functional testing.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Yes, thanks a lot for the feedback, I'll change it around a bit so it fits :)

      Delete
  4. Your procedural example is a bit of a straw-man (I.e., it's deliberately screwed up). A good software engineer would use a well-understood method such as dependency injection to avoid hard-wiring the database reference and improve the test ability.
    You can make good arguments for functional programming (which is indeed very cool) without having to make weak arguments against procedural programming. Both have heir place. Programming preferences are not a zero-sum game :)

    ReplyDelete
    Replies
    1. The idea behind the database reference is that you're changing state, and whether or not that state can be changed with dependency injection is not exactly relevant, IMHO.

      Delete
  5. You state that “These "variables" are actually functions (in most cases) with no arguments.” This is simply not true. Variables in functional programming are labels akin to variables in mathematics, rather than cells in memory. Additionally, Haskell's type system is extremely clear about what is a function and what is not. Int is very distinct from, say, () -> Int.

    Conal Elliot explains it quite well in this post. http://conal.net/blog/posts/everything-is-a-function-in-haskell

    ReplyDelete
    Replies
    1. Ah, I see.

      Thanks for the info, will update.

      Delete
  6. I think I got lost after monands. When unit testing how do you let a function annotated as a monad give out mock values?

    ReplyDelete
    Replies
    1. Yes, you would unit test it just like in an imperative language.

      Delete
    2. Yes I know you will unit test it. But how/where does a monad get its mock implementation?

      Delete
  7. This comment has been removed by a blog administrator.

    ReplyDelete
    Replies
    1. Hi. I spend time writing these entries, I absolutely hate people who spam in the comments.

      Delete
  8. I think Functional programming is a basic theme of computer science. So we should learn this program-me to do any Computer Solusion .

    ReplyDelete