Saturday, October 10, 2009

Beautiful Architecture Chapter 13: Object-Oriented Versus Functional

I found this chapter pretty interesting because the functional and OO debate is alive and well in most of the communities of which I am a part. I feel that this chapter was pitting functional against OO in an all-or-nothing matchup, whereas most of the communities that I follow talk about what specific set of constraints on a problem give functional languages an edge. When I think of functional languages in the enterprise, I do see a place for them but only as libraries to help with certain specific problems. However, I would never think to build a major pillar of an enterprise architecture in a pure functional language. As we saw from the survey that Jerry put together, 100% of the people in our class think that OO is better for the enterprise, and though it is a biased group, there were several respondents who claimed to have experience with functional languages.

I think that in comparing functional languages with OO purely on the terms of modularity is doing a major disservice to functional languages. Meyer does call out that he is ignoring the elegance of a declarative programming style of the equivalent imperative one. Although I am not a big fan of developing purely in a functional language, I respect functional languages for the positive impact that they have had on most modern OO languages. Being a C#/.NET developer by day, most of my examples will focus on that. C#’s support for (anonymous) delegates and declarative programming via extension methods (e.g. LINQ) make the code I write on a daily basis extremely flexible and concise without sacrificing readability. I do things similar to Meyer’s example of making the visitor pattern much more concise via agents.

Meyer also mentions lazy evaluation as a nice feature of functional languages. However, C# fully supports this in a managed OO language via the “yield return” command. That being said, I don’t want to put functional languages down as being useless in the face of C# because I appreciate the fact that this incredibly useful feature was most likely inspired by the functional programming style.f

In the situations that Meyer describes functional languages lacking, such as the fact that a time module that only parses ISO8601 basic cannot be easily extended to support ISO8601 extended, I agree with Meyer and would definitely use OO languages in such a situation. That seems like an almost absurd shortcoming of functional languages.

Meyer makes a good point when he says that it is unnatural to not have a concept of state anywhere in the system. As Danny mentioned during the conversation, when you paint a house red, you don’t make a copy of the original house with red paint and end up with two houses, you just change the original one. I’ve heard people say ad nauseum that immutable objects help with parallel programming. While I’m sure this is true in some respects, I don’t think that it happens automatically. There are many scenarios where you truly do need to share state between threads, and if a change creates a new object, both threads will need to be synchronized in some manner to both use the new object.

Command-query separation is something that I try to adhere to in my day to day programming. As Meyer mentions, I feel that the referential transparency helps with the readability and maintainability of the code. When hidden side effects happen from a query operation, it can be very difficult to tell from reading a client of the guilty method. The only time that I return a value from a state-modifying command is when it’s some sort of error code or success/failure flag.

Eiffel’s nonconforming inheritance sounds, which disallows polymorphism, sounds extremely bizarre to me. I definitely plan on reading up on Meyer’s decision on doing things that way in Eifel since he seems to have many interesting ideas around OO design from various books and articles of this that I’ve read, though I do often disagree with him or find his coverage to be a bit academic and verbose.

Meyer mentioned a benefit of OO languages being that they support dynamic binding and thus “remove the need for client classes to perform multibranch discriminations to perform operations.” Although the pattern matching statements that functional languages like OCaml provide are extremely powerful, I find that they can be much more verbose and, as a result, more confusing compared to a solution that used dynamic dispatch.

1 comment:

  1. Let's start with modularity. His Scylla from page 323 is, "Use a module inclusion facility...but you end up loaded with a bigger baggage than necessary, which complicates updates and may cause conflicts (assuming the derived module defines a new combinator or function and a later version of the original module introduces a clashing definition)." However, since he's obviously targeting Haskell here, you can simply say "import SomeModule (f1, f2, f3)" and those three functions are all you will ever import, regardless of any other changes to the module. This sort of restriction is difficult to do in
    OO languages, yes, but that's no excuse for not knowing that other languages do
    not suffer from these limitations.

    (You can--especially if you're an ML programmer--make a lot of complaints against Haskell's "module" system, but this is surely not one of them.)

    Taking a serious look at modularity, being able to use modules as functors as in ML is clearly an entire level beyond typical module systems. Whether one needs this level of power is an open question--note that Haskell, which owes an enormous amount to ML, bailed on that--but I don't know of any other language family that has anything like this.

    His complaint about the time parsing thing is nonsensical; in both OO and functional languages, you wrap the parsing function to extend it; OO just manages to make the wrapping process look slightly different.

    I'd like to make some mention of type classes here, and what they can do that, say, the more verbose interfaces in Java can't, but unfortunately I've not space for that.

    Anyway, as if all this weren't bad enough, he starts to fail in a major way when it comes to discussing state. Haskell has state all over the place, left and right. It's ridiculous to say that there's "no concept of state anywhere in the system" when Haskell has syntactic sugar specifically for dealing with state (among other purposes).

    It's not the lack of state that makes Haskell programs great (though removing state wherever it occurs, if you can, is always nice); it's the control over where you have state, and being able easily to limit it to certain areas and make sure it doesn't escape, that makes Haskell plus monads brilliant. Simon Peyton Jones was not wrong when he called Haskell "the world's finest imperative programming language."

    And that's why, in a major project in Ruby, I realized many thousands of lines of code in that immutable "value objects" were a great idea, not to mention that I could take advantage of the flyweight pattern if I needed to. It was only years later that I realized that this happened to be something that came out of the functional programming community.