I've been looking at Scala, OCaml, and F# recently and I've really been struck by their different approaches toward mixing OO and FP. I can't say that I like any particular approach, really. OO and FP do seem to be odd bedfellows in a language and I think that I see why now. They use syntax in rather different ways. If you aim for one unified syntax in hybrid FP/OO languages the things that you do to tune the syntax toward one end seem to detune from the other.
Let's take a look at how we structure function calls/applications across several languages:
C: a(b,c);
Java: b.a(c);
Smalltalk: b a: c
Lisp: (a b c)
Haskell: a b c
The C syntax is a straight procedural. We have a function a and we need to pass b and c along. The Smalltalk snippet is in the object-oriented style. We privilege b and say that it owns the a method. We get polymorphic substitutability in the bargain. Lisp doesn't privilege any particular argument, so it is sort of like procedural, although people do write OO code in that style in Scheme and CLOS. The Haskell example, however, is very interesting syntactically, because it is tuned to make this natural:
let x = a b in x c
In this example, we've bound x to the partial application of a to b and then used it to form an expression x c which is semantically the same as a b c.
Partial function application is powerful. People working in Haskell and the other ML-derived languages use it routinely to compose operations. It's a shame, though that its syntactic style is so different from the OO style. Here's an example that drives it home. Imagine that we want to get the second element of a list in a couple of hypothetical languages. Here's a Haskell-ish way to do it:
head (tail [1,2,3])
and here's the OO-ish way:
[1,2,3].tail.head
See the difference? FP is more of a prefix style, while OO is postfix. One could say that this is arbitrary, that you could have an OO language which uses the prefix style, but I'd argue that there is something deeper here.
Let's imagine what our code would be like if we attempted to do partial function application with OO syntax.
let remainder = [1,2,3].tail in remainder.head
The partial function application doesn't really buy us much here. Partial application is powerful when it composes functions. Often the data that we are working with is the least interesting thing to compose, yet it comes first in OO expressions and the natural style of partial function application in language design is to keep the first N terms and leave off some trailing ones.
It does seem that functional programming gets some advantages from its prefix syntax. Scala, F#, and OCaml do show that you can mix the styles, but you can't really get a unified one without picking the prefix form or the postfix form. The ML derived languages have definitely found a syntactic sweet spot and it does seem like it's a poor mix with object orientation.
Michael, I see that you have fixed the Haskell example (head tail
[1,2,3] => head (tail [1,2,3])). I guess you need to find another
example as there's no partial application (currying) here. :-)
One such example:
In Haskell we can write
contains a b
or
let x = contains a in x b
while using OO syntax we have
a.contains(b)
or
let x = lambda c => a.contains(c) in x(b)
which is quite awkward as you have to name the argument of the
anonymous function x. In other words, the FP syntax supports the
point-free style better.
Posted by: Zhanyong Wan | May 28, 2008 at 04:47 PM
Just to add the other paradigm, stack style a la Forth :
c b a
Posted by: maht | May 28, 2008 at 05:07 PM
The syntactic difference only comes with a certain form of single dispatch OO. Of course, that form is by far the most common... :)
If you look at multiple dispatch OO there's no longer a single special argument that has the power of dispatch so it's more common to put the message name first. E.g. in Common Lisp's CLOS or Clojure you end up back with (verb noun1 noun2). Some would argue whether that kind of generic function is still "real" OO, but why get into it?
I don't know how the other hybrid languages you mention handle it, but in Scala you deal with partial application using an underscore. E.g. maht's example would be written in Scala as
val x = a contains _
x(b)
It's not quite as clean and as Haskell's - but not much really is.
Posted by: James Iry | May 28, 2008 at 05:43 PM
There's another way to think about the partial function application than the simple prefix way.
Imagine your syntax supports this, for example:
let second = .tail.head in [1,2,3].second
This doesn't really give you currying (the "special argument" problem of OO still gets in the way), but it does give you composition.
Posted by: Tom Faulhaber | May 28, 2008 at 06:25 PM
I always have considered OO somewhat organizational. The vast majority of the time I want functions that operate on objects, which are really just organization of data with possibly some dynamic rendering of the data. In this light, OO and functional styles are more interchangeable. I suppose this is also why I'm attracted to Python and JavaScript. Both languages do a good job of utilizing objects as data structures while allowing functional patterns.
Posted by: Eric Larson | May 28, 2008 at 11:31 PM
-- Haskell allows you to locally
-- define or redefine your own infix
-- punctuation, and the period "."
-- can be user defined as reverse
-- application. So this works:
twos = let a . b = b a
two = [1,2,3] . tail . head
second x = x . tail . head
two' = [1,2,3] . second
in (two,two')
-- This evaluates to (2,2)
Posted by: Chris K | May 29, 2008 at 03:34 AM
maht: Thanks, but that first example was not meant to show partial application. It was just meant to show how traditional FP is a prefix style.
Posted by: Michael Feathers | May 29, 2008 at 05:04 AM
James: I'm aware of Lisp and multiple-dispatch. To me, it's really the neutral ground in the syntax space and a good place for a hybrid OO/FP language to be. We seed doomed to have these odd mixes like F#, Scala, and OCaml, however.
Posted by: Michael Feathers | May 29, 2008 at 05:06 AM
Tom: While I was mulling the idea of this post I came across that idea also and thought about posting it later. I've never come across a language which does that (although I haven't paid any attention to the concatenative ones). It does seem like a good feature, though.
Posted by: Michael Feathers | May 29, 2008 at 05:08 AM
I think this is because most *mainstream* OO languages don't let you deal with messages as objects. You have objects. You can send them messages. But messages themselves can't easily be manipulated.
The Higher Order Messaging technique lets you do partial application of messages but binds the partially applied message to a single object.
Posted by: Nat | May 29, 2008 at 05:44 AM
More than the syntax, I think far more interesting are the *paradigm* differences between OO and FP. Particularly, how to arrive to a solution based on the description (or lack of thereof) of the problem
Posted by: Gabriel C | May 29, 2008 at 01:28 PM
Beyond syntax, at its heart OO is a specific model of the data, not unlike a type of lego blocks or even a subset of the tetris blocks (like the S ones). If you see the world as this odd-ball combination of static data and dynamic methods, that will influence how you try to jam it into a computer. If you see it as just syntax for disconnect variables that is a different perspective. From typedefs in C to vectors and matrices in APL, I suspect that the decomposition of data into primitives hugely effects how the problems are encoded by programmers. Mixing and matching is like having a conversation in multiple spoken languages all at once, extremely confusing to all but a very select group of people.
Paul.
http://theprogrammersparadox.blogspot.com
Posted by: Paul W. Homer | May 29, 2008 at 09:07 PM
Nice article! I've already talked about a relative point on my blog on
http://sadekdrobi.com/2008/06/01/and-you-get-all-the-vm-libraries-for-free-is-it-actually-what-i-want-when-i-switch-languages/
This post however treats syntax which is another important deffect of a the promise of a seamless integration.
Thanks Michael
Posted by: Sadek Drobi | June 27, 2008 at 06:10 AM