Functional programs rarely rot

The way that most of us sell functional programming is all wrong. The first thing we say about it is that the style lacks mutable state (although most functional languages allow it). So we’re selling it by talking about what it doesn’t have. Unfortunately, it’s not until late in a programmer’s career (and some never get there) that he realizes that less power in a language is often better, because “freedom-from” can be more important than “freedom-to” in systems that will require maintenance. This makes for an ineffective sales strategy, because the people we need to reach are going to see the statelessness of the functional style as a deficit. Besides, we need to be honest here: sometimes (but not often) mutable state is the right tool for the job.

Often, functional programming is sold with side-by-side code snippets, the imperative example being about 30 lines long and relatively inelegant, while the functional example requires only six. The intent is to show that functional programming is superior, but the problem is that people are likely to prefer whatever is most familiar to them. A person who is used to the imperative style is more likely to prefer the imperative code, because they are more used to it. What good is a reduction to six lines if they seem impenetrable? Besides, in the real world, we use mutable state all the time: for performance and because the world actually is stateful. It’s just that we’re mindful enough to manage it well.

So what is it that makes functional programming superior? Or what is it that makes imperative code so terrible? The issue isn’t that state is inherently evil, because it’s not. Small programs are often easier to read in an imperative style than a functional one, just as goto improves the control flow of many small procedures. The problem, rather, is that stateful programs evolve in bad ways when programs get large.

A referentially transparent function is one that returns the same output per input. This, and immutable data, are the atoms of functional programming. Such functions actually have precise (and usually, obviously intended) semantics which means that it’s clear what is and what is not a bug, and unit testing is relatively straightforward. Contrast this with typical object-oriented software where an object’s semantics are the code, and it’s easy to appreciate why the functional approach is better. There’s something else that’s nice about referentially transparent functions, which is that they can’t be changed (in a meaningful way) without altering their interfaces.

Object-oriented software development is contrived to make sense to non-technical businessmen, and the vague squishiness associated with “objectness” appeals to that crowd. Functional programming is how mathematicians would prefer to program, and programming is math. When you actually care about correctness, the mathematician’s insistence on integrity even in the smallest details is preferable.

A functional program computes something, and intermediate computations are returned from one function and passed directly into another as a parameter. Behaviors that aren’t reflected in a returned value might matter for performance but not semantics. An imperative or object-oriented program does things, but the intermediate results are thrown away. What this means is that an unbounded amount of intermediate stuff can be shoved into an imperative program, with no change to its interface. Or, to put it another way, with a referentially transparent function, the interface-level activity is all one needs to know about its behavior. On the other hand, this means that for a referentially transparent function to become complex will require an equally complex interface. Sometimes the simplicity provided by an imperative function’s interface is desirable.

When is imperative programming superior? First, when a piece of code is small and guaranteed to stay small, the additional plumbing involved in deciding what-goes-where that functional programming can make the program harder to write.  A common functional pattern when state is needed is to “thread” it through referentially transparent functions by documenting state effects (that may be executed later) in the interface, which can complicate interfaces. It’s more intuitive to do a series of actions than build up a list of actions (the functional style) that is then executed. What if that list is very large? How will this affect performance? That said, my experience is that the crossover point at which functional programming becomes strictly preferable is low: about 100 lines of code at most, and often below 50. The second case of imperative programming’s superiority is when the imperative style is needed for performance reasons, or when an imperative language with manual memory management (such as C or C++) is strictly required for the problem being solved. Sometimes all of those intermediate results must be thrown away because there isn’t space to hold them. Mutable state is an important tool, but it should almost always be construed as a performance-oriented optimization.

The truth is that good programmers mix the styles quite a bit. We program imperatively when needed, and functionally when possible.

Imperative and object-oriented programming are different styles, and the latter is, in my view, more dangerous. Few programmers write imperative code anymore. C is imperative, Java is full-blown object-oriented, and C++ is between the two depending on who wrote the style guide. The problem with imperative code, in most business environments, is that it’s slow to write and not very dense. Extremely high-quality imperative code can be written, but it takes a very long time to do so. Companies writing mission-critical systems can afford it, but most IT managers prefer the fast-and-loose, sloppy vagueness of modern OOP. Functional and object-oriented programming both improve on the imperative style in terms of the ability to write code fast (thanks to abstraction) but object-oriented code is more manager-friendly, favoring factories over monads

Object-oriented programming’s claiming virtue is the ability to encapsulate complexity behind a simpler interface, usually with configurability of the internal complexity as an advanced feature. Some systems reach a state where that is necessary. One example is the SQL database, where the typical user specifies what data she wants but not how to get it. In fact, although relational databases are often thought of in opposition to “object-oriented” style, this is a prime example of an “object-oriented” win. Alan Kay’s original vision with object-oriented programming was not at all a bad one: when you require complexity, encapsulate it behind a simpler interface so that (a) the product is easier to use, and (b) internals can be improved without disruption at the user level. He was not saying, “go out and write a bunch of highly complex objects.” Enterprise Java is not what he had in mind.

So what about code rot? Why is the functional style more robust against software entropy than object-oriented or imperative code? The answer is inherent in functional programming’s visible (and sometimes irritating) limitation: you can’t add direct state effects, but have to change interfaces. What this means is that adding complexity to a function expands its interface, and it quickly reaches a point where it’s visibly ugly. What happens then? A nice thing about functional programming is that programs are build up using function composition, which means that large functions can easily be broken up into smaller ones. It’s rare, in a language like Ocaml or Clojure when the code is written by a competent practitioner, to see functions longer than 25 lines long. People break functions up (encouraging code reuse) when they get to that point. That’s a really great thing! Complexity sprawl still happens, because that’s how business environments are, but it’s horizontal. As functional programs grow in size, there are simply more functions. This can be ugly (often, in a finished product, half of the functions are unused and can be discarded) but it’s better than the alternative, which is the vertical complexity sprawl that can happen within “God methods”, and in which it’s unclear what is essential and what can be discarded. 

With imperative code, the additional complexity is shoved into a series of steps. For-loops get bigger, and branching gets deeper. At some point, procedures reach several hundred lines in length, often with the components being written by different authors and conceptual integrity being lost. Much worse is object-oriented code, with its non-local inheritance behaviors (note: inheritance is the 21st-century goto) and native confusion of data types and namespaces. Here, the total complexity of an object can reach tens of thousands of lines, at which points spaghettification has occurred.

Functional programming is like the sonnet. There’s nothing inherently superior about iambic pentameter and rhyming requirements, but the form tends to elevate one’s use of language. People find a poetic capability that would be much harder (for a non-poet) to find in free verse. Why? Because constraint– the right kind of constraint– breeds creativity. Functional programming is the same way. No, there’s nothing innately superior about it, but the style forces people to deal with the complexity they generate by forcing it to live at the interface level. You can’t, as easily, throw a dead rat in the code to satisfy some dipshit requirement and then forget about it. You have to think about what you’re doing, because it will effect interfaces and likely be visible to other people. The result of this is that the long-term destruction of code integrity that happens in most business environments is a lot less likely to occur.

About these ads

31 thoughts on “Functional programs rarely rot

  1. While I agree that Java can promote some really bad code, I’m not sure that it’s fair to lay that at the feet of object-oriented programming.

    Inheritance is overused in many places and OO is usually much harder to test, whereas functional programming is incredibly provable. However there are places where a functional paradigm just doesn’t match the requirements (unless you’re writing code for mathematicians).

    Code that interacts with human beings has complex behavior. For example, when I click “Post Comment” below it will kick off a bunch of things. It will add my post to the database. It may email people who’ve posted above me. It may email you. It may check to see if you’re on the site in order to give you a popup message.

    Each of those could be written as an individual function, and most of the code would fit well into that paradigm, be clean code, and be very testable.

    But running that sequence of events does not lend itself well to a functional style. It’s an imperative sequence: first do this, then do that, then do this if something is true.

    Thinking about your spaghettification post, you can consider most unix programs to be “functional” – they have a small set of inputs (often one) and a small set of outputs (often one). And in that case most people use a shell script to do complex things with those simple functions. Shell scripts are about the most imperative code I can think of.

    Imperative code is necessary. Object-oriented is just a way of organizing that code. Organizational systems are difficult to do well, be they in code or otherwise. They tend towards bureaucracy and complexity, but they let you find things. Two examples: DNS and TCP/IP. Bureaucratic, complex, and necessary.

    I think that OO stylistically looks better, but I agree that’s probably dependent on the reader. I prefer the ruby syntax for mapping over an array:
    a.map &b
    as opposed to python’s:
    map(a, b)
    To me the ruby way is clearer because it gives context to the call. Again, my personal preference.

    OO is a tool like any other – useful but dangerous if used wrong.

  2. It’s rare, in a language like Scala or even Java when the code is written by a competent practitioner, to see classes longer than 25 lines long.

    Your error here is to compare the best function code with the worst OO code. If you think that in ten years time the world is not going to be full of really dire functional code written by people who never cared enough to learn good programming principles (that cut across issues of OO or FP), then you are being wonderfully naive. There will be no shortage of 1,000-line functions just as there is not shortage of 1,000-line methods now. Neither say anything meaningful about the paradigm in which they are written, as neither was written without any real though about the paradigm.

    Code doesn’t rot because it is written in an OO or FP language. It rots because it is not treated by the programmer as a living work that needs maintenance and periodic re-evaluation and re-structuring.

    I really fear that the whole false dichotomy of the superiority of FP over OO, or the other way around, is leading to a religious war that is blinding us all to the fact that both are demonstrably valuable ways of thinking about software, address mostly orthogonal concerns about abstraction and provide useful tools that can be used in conjunction with each other, rather than existing in conflict.

    • I agree. This whole push towards functional programming is interesting, but there is a reason OO programming is still being used and it is used over the functional or procedural paradigm: It models the real world much more effectively. The recent Mars landing spacecraft was written in an OO language by _scientists_. No one is infallible, but I would venture to guess that modeling the real world was more important to them than even academic concerns.

      Most people do not think in the functional style. It is true that computer _science_ comes from math, but real world computer _engineering_ is more like writing a book or short story than math in most domains (not all). That is why it needs to be “treated by the programmer as a living work that needs maintenance and periodic re-evaluation and re-structuring.” Bjarne Stroustrup has a very good take on it and the reason there are multiple paradigms included in C++ (and no, it’s not so they can all be used at once).

      I’d like to see some open source examples of purely functional apps for blogs or a cms or something relatively simple. Showing me that functional programming can calculate the Fibonacci sequence in less lines of code and a much cleaner way doesn’t mean anything.

      • Much of what you say is true, but many programs are NOT about simulations or real world behavior.

        Besides that, even Haskell can write applications that simulate behavior.

        The fact that NASA used an OOP language for such a type of application is not surprising, especially considering the fact that they are a large organization and have to just pick whatever languages the organization approves. I’d wager C++ was probably one of a limited list of choices.

        • Regarding examples:

          Functional programming is well suited to a lot more than just toy examples than fibonacci or quicksort. I agree that examples like that are not impressive, since they don’t do anything useful, but there are a lot of event driven applications out there, at least open source ones. I don’t blame you for bringing that up, since the Haskell wiki *still* boasts about a concise quicksort algorithm, which imo makes the community look impractical, when functional programming is very useful.

          For web applications off the top of my head, I used an RSS Feed reader that was written in Ur/Web, (http://bazqux.com/) which is purely functional, and while it was mostly a tech demo, it worked very well the last time I used it. For a very web 2.0 application, Hayoo (http://holumbus.fh-wedel.de/hayoo/) is a pretty good example. Also, facebook chat and ejabberd (one of the most popular jabber servers) are written in Erlang.

          As for various event driven applications, xmonad, manatee, yi, and so on are written in Haskell, and I’ve personally written a very featureful email client in Erlang, and a few desktop type applications in Scheme.

          These are just a few examples here and there — there are hundreds more, I just listed stuff off the top of my head.

          Basically, functional programming is general purpose and is well suited towards most domains, not just mathematics applications or ivory tower stuff. It isn’t ideal for stuff like simulations, but a language like Erlang for example could easily be used for Actor model type programs.

      • When you say “most people do not think in the functional style”, perhaps what you really mean is that “people who program in an imperative or OO language don’t think in a functional style”. It’s your own fault if the only examples of functional programming your aware of are things like how to calculate a Fibonacci sequence. Companies such as Jane Street and Credit Suisse make extensive use of functional programming languages (Ocaml and F# respectively). It’s not hard to find examples across many different domains.

        It’s very hard to introduce a functional language inside a large company, no matter what the potential benefits are. What I’ve tried to do is create a number of F# based tools that use functional features such as default immutability, algebraic data types, pattern matching and so on. By creating a number of useful tools that can save hours or days of manual labour, the other developers can see examples of some real-world functional techniques that go far beyond Fibonacci sequence generation. I’ve found this to be a good low risk way of introducing functional languages inside conservative companies with entrenched use of mainstream OOP languages.

        • What I mean is that most people are oriented towards natural spoken / written language and OOP and even more so, procedural paradigms are closer to natural language than mathematics in real use. This is intentionally, by design.

      • Limit to about 3-5 methods and that works. Of course, it’s just a general guideline. After all, this isn’t to encourage stuff like nested ternary or lack of { } usage around one line if’s (in C type languages), it’s more of guideline and all guidelines are meant to be broken.

  3. “An imperative or object-oriented program does things, but the intermediate results are thrown away. What this means is that an unbounded amount of intermediate stuff can be shoved into an imperative program, with no change to its interface.”
    Shoving in is usually done for human readability (which is a requirement that should take precedence in every programming language – “Programs should be written for people to read, and only incidentally for machines to execute.”). It must be the optimizer’s job to get rid of the intermediate results and to “un-shove” them out of the execution. “Shoving in” is therefore putting the human effort for better use.

    “It’s rare, in a language like Ocaml or Clojure when the code is written by a competent practitioner, to see functions longer than 25 lines long. People break functions up (encouraging code reuse) when they get to that point. That’s a really great thing! Complexity sprawl still happens, because that’s how business environments are, but it’s horizontal. As functional programs grow in size, there are simply more functions.”
    …but structurally there just isn’t any need for those functions to be that many, isn’t it?. A great deal of functions that don’t justify their individuality still have to be remembered by the programmer and to occupy mind index space, or in other words – to bother.

    • I could be wrong, but what you are describing seems to be the functional equivalent of over-architected OO (or procedural) style applications. You’re right human readability is #1 and reducing complexity by association then becomes the #1 procedure to ensure human readability. If you’re not reducing complexity as much as possible, no matter what paradigm you use, you will reach the giant unmaintainable program state.

      The real problems is that code reuse has taken a back seat (even though it doesn’t seem to have done so) these days and the power of encapsulation is thus reduced. Most programmers these days don’t even understand that one of the ideas behind OOP is code reuse.

      • Personally, I understand fully that one of the ideas behind OOP is supposed to be code reuse. I just think it’s a phenomenally bad attempt to accomplish this.

  4. It would be interesting (albeit impossible) to verify how much of this observed effect is due to the inherent properties of FP vs. external selection factors. For instance:

    - projects / companies that are willing to use a technology considered “out of the mainstream” may be less likely to produce requirements that call for the proverbial dead rat to be dropped. An FP project that had management try to apply the “buy ten copies of the book to read it faster” methodology would likely turn into a mess.

    - very few people have FP forced on them; there’s likely a self-selection effect here where only developers who think about code and *care* about things like code quality end up learning FP.

  5. Essentially you are saying that it is better to write more, smaller, simpler subroutines than fewer, larger, complex ones. I fully agree with that.

    However, this is called a good engineering practise and it is not as you seem to believe a property of any programming paradigm, nor is any programming paradigm immune from non-adherence to this good engineering practise.

    You base your claim on anecdotal evidence, comparing what you say is the length of a typical method in Java to that of a typical function in Haskell. This is flawed because one can pick a different imperative language that surpasses Haskell in the discipline of many-short-simple-subroutines.

    Forth, for example would beat Haskell hands down in that discipline. A well written Forth program may have hundreds, or even thousands of subroutines, called words, most of which are no longer than five lines of code and have no more than one or two parameters.

    So, what does your comparison prove? Not what you say it does.

    What you are talking about has nothing to do with the paradigms. Instead it comes down to a phenomenon that is well known in linguistics. Whenever the group of language users changes dramatically, the use of the language changes dramatically, too. In natural languages, the influx of a large body of non-native speakers tends to lead to corruption and ultimate change of a language. What was once considered “wrong”, initially becomes merely acceptable and eventually it develops into a new standard.

    It is doubtful that the initial Java practitioners at Sun have written bloated code such as we see today in Java. The bloat that is typical of Java today is most likely the result of a change in the user base, in other words by the language becoming mainstream. The bad news here is that should Haskell ever enjoy the kind of widespread use of Java then good engineering practises will be increasingly neglected and result in a drop of quality of the code that its practitioners produce.

    Some languages appear to be more resistant to this kind of abuse than others due to their restrictive nature. But this is not in fact a resistance against abuse, it is a resistance to accumulating a large user base, which in turn keeps standards higher than otherwise. The very fact that a language is so much more restrictive ensures that only the most disciplined practitioners will want to use it and that prevents the language from going mainstream.

    Interestingly though, disciplined practitioners who prefer to work with a restrictive language would also produce quality code in less restrictive languages.

    The moral is this: Disciplined and experienced practitioners have a much larger impact on quality than the tools we all use. We would therefore be well advised not to argue too much which languages everyone use, but more about how important discipline and good engineering practises are, how much more important they are than languages and paradigms.

  6. Pingback: Functional programs rarely rot | Functional programming | Scoop.it

  7. This is why I enjoy working with Ruby. It has all the elements of an expressive, functional language, but is also 100% object oriented. So you can create small objects (like 5-10 lines of code) that do one thing, and one thing well, but also hold state about themselves.

    I’m a big fan of the middleware pattern for this reason. You can define a bunch of classes that respond to a simple API, then just define an array and execute the API on them in order. It’s imperative, but allows you to easily manipulate the order of the functions being called, and allowing you to test both the synchronicity of them all being called together and each class in and of itself.

  8. Pingback: My next “language of choice” « LowSNR

  9. “for a referentially transparent function to become complex will require an equally complex interface.”

    and

    “referentially transparent functions… can’t be changed (in a meaningful way) without altering their interfaces.”

    Can you explain what you mean by this? I can’t see how either of these can be true. Suppose I have a function f(x), which takes a real number and returns a real number. This function could be something simple like incrementing x, or it could be very complex, calculating an integral, which uses heuristics to determine which of a number of sub-functions to call.. In both cases, any given input will always produce the same output, and there is no internal state saved. The interface is identical in both cases, and I could very easily change the functionality without altering the interface at all.

    • By “interface” I mean observable behavior, not type signature. Both functions are R -> R, but they perform different mappings.

      People can still make breaking changes, for sure, but my point is that the typical cases of complexity-accretion become breaking changes because they have to change the interface to be visible. Whether that becomes visible depends on the type system and the quality of unit testing (which is as necessary in statically-typed languages, as code gets large, as in dynamic ones).

  10. OO is probably way easier to grasp and use than functional paradigm, and therefore attracts many inferior developers which then develop the spaghetti code. It doesn’t help that OO makes it really easy to structure your code in a wrong way.

    However, if you read some of the literature on proper OO design, they will all favor some of the concepts you attribute to functional paradigm, such as very small functions, small classes with clear responsibilities, immutable classes and side effect free functions/methods as much as possible, and mutable state only when called for.

    In my view, OO brings a couple of very nice concepts such as encapsulation, composition and interfaces vs implementation distinction (inheritance really sucks). These concepts allow good programmer to break a complex solution into small, loosely coupled units which are then easy to analyse, modify, reuse and extend.

    After two years of coding in Erlang which is not even a pure functional language, despite being thrilled with both the platform and language, I really miss some of OO features (mutable variables not being one of them).

  11. Pingback: Resources for Getting Started With Functional Programming and Scala | StackMob

  12. Pingback: Maintenance and Coding Standards | Functional Engineering

  13. Pingback: ADVANTAGES OF PYTHON « raytechs

  14. The world is not mutable. It may be from the PoV of pure science and physics (and is it…? provably and beyond doubt?) but that doesn’t mean that we need to mimic this in software. That is an extremely widespread misconception that ignores the notion of uncoordinated perception and the time dimension.

    Take a person standing on some coordinates. The person than moves and I assume you’d like to model this by having a mutable Person object (record, whatever) with properties x and y and you’d like to set those properties to some new coordinates as the person moves… correct? But then what happened to the old value of the identity called Person? There are no old values, “old” has no meaning here. The time dimension does not exist. And why do I care? Well… can – in the real world – the identity control the perception process of the onlookers? Can it prevent it? Do they have to freeze the Person in order to do their work? No. I, as an observer of Person, can take a “snapshot” of the Person at whatever coordinates I want and I can ponder it forever… do ANYTHING with it. When you model the reality using shared mutable state, devoid of the time dimension, you are defying the way interactions in the real world actually work. Perception is free, nonblocked and uncoordinated and immutable and persistent data structures are the best way of expressing this. Yes, there is an identity, that has various values over time. But that does not mean it is one mutable value. River is not a mutable object. “River” is an identity that we superimpose on a natural phenomenon. Heraclitus knew this long ago…

    Sure, there is the problem with computers having been vonneumannized for a long time. Turing won. But does it mean we need to obey this on all layers, down to assembly code…? I don’t think anyone is seriously taking this as an argument (except for performance pragmatism where performance is a concern – which mostly isn’t, yet people think it is and build steaming piles of horse manure because of that).

    This is one of the reasons functional programming (in its basic principes) is simpler than OOP. Simpler, not easier. I may not be explaining it that well… Stuart Halloway and Rich Hickey are: http://www.infoq.com/presentations/An-Introduction-to-Clojure-Time-Model

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s