Ask your average Java (or C#) programmer to ‘write a method that takes a list of integers, adds 10 to each element, converts the result to a String, prepends *** to it and returns the resulting list’. It’s quite easy and you can certainly postulate what kind of start would be made on this. Here is how I suspect most Java programmers would solve this problem:
...
static List addTenAndConvert(List list) {
List result = new LinkedList();
for(Integer i : list) {
String s = "***" + String.valueOf(i + 10);
result.add(s);
}
return result;
}
Simple enough. Many people would also write so-called ‘unit tests’ to assist in verifying that the method meets the specified requirement. It is interesting to note at this point that ‘for all’ (hint) list arguments, the length of the return value will always be equal. So for the argument, {1,2,3} with a length of 3, the result is {“***11″, “***12″, “***13″} also with a length of 3 and this property holds across all lists.
But then, I ask for another method that does all the same, but this time, prepends “AAA” instead of “***”. Whatchya gunna do? Copy/paste/change? No, you’re going to refunctor (to be defined in time to come), that’s what. You might pass an additional argument of type String and prepend that. Sounds fair enough. But then I will ask instead that you append “BBB” instead of prepending “AAA”. Pass an additional argument of type boolean?
Let’s take a look at what we’ve got:
...
static List addTenAndConvert(
List list, String s, boolean prepend) {
List result = new LinkedList();
for(Integer i : list) {
String ss = prepend ? s + String.valueOf(i + 10) :
i.toString() + s;
result.add(ss);
}
return result;
}
But then, I ask you for another requirement — this time devastating. I ask instead that it is not a List argument, but a List argument and a List return type. Going to copy/paste this time, right? The conversion from each String element to the new Integer element is left unspecified for now, but feel free to dream something up. In fact, feel free to ‘refactor’ it out:
interface Convert {
Integer convert(String s);
}
…then change the method like so:
...
static List stringListToIntegerList(
Convert c, List list) {
List result = new LinkedList();
for(String s : list) {
Integer i = c.convert(s);
result.add(i);
}
return result;
}
Notice that our method’s name is becoming less specific as it is refactored and becoming more abstract in behaviour. Further, we cannot write our method addTenAndConvert in terms of our method stringListToIntegerList by passing a different implementation of Convert because our types do not match. However, notice that the transformation on each list has nothing to do with specific types — these types are actually unbounded polymorphic types. Let’s try again.
interface Convert {
U convert(T t);
}
static List fooForNow(Convert c, List list) {
List result = new LinkedList();
for(T t : list) {
U u = c.convert(t);
result.add(u);
}
return result;
}
How’s that look!!? What shall we call this method? I have called it fooForNow, but if you’ll just let me call it map instead, just because. We can now write our method addTenAndConvert with our newly found abstraction — the map function:
static List addTenAndConvert(List list) {
Convert c = new Convert() {
public String convert(Integer i) {
return "***" + String.valueOf(i + 10);
}
};
return map(c, list);
}
How funky is that? Funky enough for you to start looking at funktional programming? I’ll let you in on a little secret. This relatively high euphoric point of using Java/C# (et. al.) is the very basis and starting point of most functional programming languages. In fact, the map function is typically included in the standard libraries! Functional programming is Java version 42, seriously. It is a natural extension to what it is that most Java/C# programmers are already doing. It is not some esoteric, orthogonal, unrelated paradigm that has nothing to do with anything except the point of singularity.
If we consider our Convert type to actually represent ‘anything that can convert a T to a U’, we call map a higher-order function, since it takes a function (from T to U) as an argument. Whether or not to name this function argument (to Convert in our example) and hide the behaviour of this conversion behind some implementation sets the premise for the ‘DD (Dynamic Dispatch) versus HOF (Higher-Order Function) issue’.
If we look at the type for the Haskell map function, we see it like this:
map :: (a -> b) -> [a] -> [b]
That is, as the first argument, it takes a function from a type ‘a’ to a type ‘b’ (we called these T and U, but Haskell type parameters must be lower-case). The second argument is a list of the type ‘a’ and the return type is a list of the type ‘b’. That’s exactly what we just wrote in 43 trillion lines of Java code! OK, maybe I am exaggerabating a bit there.
And in case you’re wondering, here is how addTenAndConvert looks in Haskell:
let addTenAndConvert = map (("***" ++) . show . (+10)) -- tidy eh?
Why ‘Refunctoring’? First, I refuse to reuse euphemistic terms that have served only to denigrate the standard of the software development industry, therefore, I cannot use ‘Refactoring’ (oooh! did I just say that!!?). To show this, consider the fact that the entire notion of ‘Refactoring’ is done away with one simple phrase ‘Functional Programming’ in the case shown and hundreds more. Of course, you might decide to redefine Refactoring to a different context, but then, redefining terms often leads to confusion (the term ‘function’ is itself a perfect example). To further demonstrate this point, open the book or catalog on Refactoring and turn to the section titled, ‘Replace Parameter with Method’. Then say out loud, ‘partial application’ — if you don’t know why you’re saying that, learn what partial application is, then perhaps it might dawn on you. A useful exercise is to apply this technique to all so-called ‘Refactorings’ and see how many are eliminated by existing programming techniques that don’t get very much air time in advertising material (though, this might be changing thankfully). More useful might be to see how many are left standing (want to know my conjecture?).
Anyway, Refunctoring is what most people really are doing. They are converting their code that is written in not-too-powerful programming languages to mimic the power that is already available in functional programming languages. They are representing higher-order functions with (importantly, co-variant) polymorphic type parameters, refactoring out what partial application already provides and much more. This is being done, despite having been invented many, many years ago, but the fact that it is being done mandates a name, albeit how absurd it might be. I use the term Refunctoring, for no other reason than it was suggested to me by a bemused colleague (you know who you are
) one day and upon reflection, I have decided that it is entirely appropriate.
You don’t think the map function is a special case do you?
Make it work.
Make it right.
Make it fast.
A motto to code by.
caverdude
Exactly, what I was explaining to our interns today.
Functionnal programming is a very efficient way to abstract functionnality, and therefore to respect the DRY principle.
I think the “Refactoring” book is really nice, and I don’t think the phrase is too catchy. However, you have to keep in mind that all the refactorings are mostly for Java. The time I read it, it was a really enlightening experience.
In Haskell, much of the knowledge is scattered in papers here and there, there’re a lot of tricks to learn–but you mostly find them by accident. I think it would be really nice to have a similarly practical guide to Haskell programming.
Nice one. Just to add that in C# these things are more easy. The power of generics with anonymous delegates is amazing.
The point here is to identify that even iterating over a for-loop is duplication, and programming functionally helps you to even abstract that and allow reuse in various forms.
> So for the argument, {1,2,3} with a length of 3, the result is {”***10″, “***11″, “***12″}
Mmmm since when does 1 + 10 make 10
Even easier in C# 3.0:
static IList AddTenAndConvert(IList list) {
return map(x => “***” + (x + 10), list);
}
Since, since…!!
Thanks Leonh — updated accordingly.
Or python list comps:
def addTenAndConvert(listOfNumbers):
return [ "***"+str(x+10) for x in list ]
Of course, python also has map, but we generally prefer list comps for clarity and power.
Functional programming is not a suspicious idea that has to prove itself. It is simply the act of taking functions, the most fundamental idea in computing, seriously.
It bothers me that C# apparently introduces dumb new names like “anonymous delegate” for familiar, old ideas. Nevertheless, I applaud Microsoft for giving developers more powerful abstractions to work with in a mainstream language than Java offers.
The Scala equivalent of Blake’s code would be:
def addTenAndConvert(list: List[Int]) = list map (n => “***” + (n+10))
Scala infers the type of the returned value. If you wanted to generalize the transformation function, you could do like:
def tranformIntList(list: List[Int], conv: Int=>String) = list map conv
And you could then reproduce the first example as follows:
transformIntList(someIntList, {n => “***” + (n+10)})
You could make the transformation function generic on the types as follows:
def transformIntList[A,B](list: List[A], conv: A=>B) = list map conv
Which would then transform a list of type A to a list of type B. And this could run on the JVM where you could, at any time, invoke any Java library(ies) you needed. Not too bad.
Ruby’s nice too… not quite that nice though
atac = lambda {|x| x.map {|y| “***#{y.to_i + 10}”}}
alist = [1,5,8,10]
atac.call alist
#=> ["***11", "***15", "***18", "***20"]
in perl6 using hyper operators:
sub *infix:($int, $prefix) { $prefix ~ (10 + $int) }
# the above declares a new operator :
pugs> 1 addTenAndPrefix ‘***’
#=> “***11″
and we can use it as an hyper operator
pugs> (1,2,3) >>addTenAndPrefix (“***11″, “***12″, “***13″)
sorry the last line is wrong
pugs> (1,2,3) >>addTenAndPrefix
hmmm
bad formatting in wordpress?
(1,2,3) >>addTenAndPrefix
http://nopaste.com/p/3K/plaintext
sorry for the flooding
This example is ridiculous. Here’s how it works in Io:
List addTenAndPrefix := method(self map(v, “***” .. v + 10))
And then to call it:
list(5, 6, 7) addTenAndPrefix
==> list(“***15″, “***16″, “***17″)
As someone who does believe in the power of functional programming, I feel such examples as that given in this blog are more harmful than helpful. Java is not the pinnacle of “non-functional” programming. The statement, “How funky is that? Funky enough for you to start looking at funktional programming?” is completely silly.
Actually, you don’t need self in the above example… not had the morning coffee yet:
List addTenAndPrefix := method(map(v, “***” .. v + 10))
in F-Script (supports Array Programming with a smalltalk syntax and works only on OS X)
> ‘***’ ++ @ (({1,2,3} + 10) @ printString)
{‘***11′, ‘***12′, ‘***13′}
Good article. It’s unfortunate that it comes across as yet another “I’m bitter we’re not all using functional languages” rant. I program in Java most of the time for a living. I’d rather program in Lisp, Python, or Ruby (I have yet to really investigate Haskell). I think the industry would certainly be a step or twelve forward if we were all using more powerful languages.
BUT: There are two ways to solve problems. Method A is by changing your context to a more suitable one (changing from programming in Java to programming in a more powerful language, for example). This is great when you can pull it off. Method B is by doing things better within your context (refactoring and unit testing rather than cowboy coding in Java, for example). Sometimes you cannot change your context. For most people, certain aspects of their context will be simply out of their control, in which case you do B.
I’m tired of these rants bashing “Java programmers” and everything related to the Java universe. Yes, most of the specific refatorings listed in Fowler’s book disappear in more powerful languages. Yes, most of the design patterns disappear in more powerful lanugages. This does not mean they “served only to denigrate the standard of the software development industry”. Far from it. They help people apply method B. Like I said, method A is not always possible, and is not the only useful way to solve a problem.
Sometimes parts of method B are useful even after you change your context. For example, I still believe “improving the design of existing code without changing functionality” (a.k.a. refactoring) and testing individual pieces of code in isolation to ensure they do what you think they do (unit testing) are both still useful ideas in a functional world.
Hi Ryan,
Nobody is bashing anyone in particular — in fact, the intent is quite the opposite — to identify with the layperson and help them make the transition to more powerful programming languages by using their language and point out that it is a natural progression, rather than an orthogonal paradigm that should be resisted.
I realise my statement, “denigrate the software industry” is stated without premise, but it is a statement that I stand by unless some seriously compelling evidence in favour of the contraposition comes to light.
I’m just as tired of those rants as you are — it was never the intent. If just one Java programmer thinks, “hey I understand that! I’m going to write my own map function in Haskell. Hey look how cool it is!”, then the primary objective is achieved.
For what it’s worth, I work for a J2EE consultancy, using Haskell, Scala and CAL. I used to “use Java for a living”, since I used to implement it for IBM; both the JDK and WebSphere at different stages.
If you’ll allow me to have a whinge, I am tired of being accused of portraying the ‘us/them’ mentality as if I am on this side of the fence and you are on the other. I have been accused of ‘not having real world experience’ (just what is that anyway?) and ‘not working on a large Java application’ (is WebSphere not large enough?), simply what I believe to be an attempt to resist having to invest time and effort into a deeper analysis of the topic at hand. It’s just foolish banter that completely ignores the opportunity for one or both parties involved to take a learning experience.
I’m tired of being accused of being ‘elitist’ or ‘arrogant’ simply because I am perceived to be attacking the lack of knowledge of my audience (not necessarily on this blog) — believe me, there are many people who have far more knowledge than I do on these topics and I make every effort to listen and reflect on what I may be able to take from them — a truly valuable exercise. If someone knows something I don’t and I am interested, I embrace the chance to be humbled, rather than erect a defence against what I might perceive as an attack. Furthermore, I encourage others to do so as well, if for no other reason, that I may be able to learn from them in the future. Yes, I am selfish when it comes to knowledge.
Maybe we should have less televisions, less wars, less cultural ignorance and things would change, but this is a very weak conjecture. Got any ideas?
I think you would seem less attacking, and more humble, if you took out the emotive parts of the posts.
For example, “Many people would also write so-called ‘unit tests’”. I have my own reasons not to use the term ‘unit tests’, primarily that I don’t think there’s much value or practicality to their being tests of a small unit, because the extra code required to accurately mock the environment around a unit is not worth it.
However, by saying “so-called” you are implying that something the reader is probably comfortable with is wrong, but not providing a reason. That puts the reader on edge, and not all readers are comfortable with that.
“I ask instead that it is not a List argument, but a List argument and a List return type.”
One might argue in favour of removing the types, if they’re getting in the way. I know what you mean, of course, but it might be more useful to speak of value-based mapping rather than mapping that primarily converts between types.
Deleted pointless comment
Why does it seem the Idiot Club visits a few weeks after publication?
True: a good number of refactorings in Fowler’s catalog move towards partial application. Still, I just don’t see how a book that helps people discover that idea “denigrate[s] the standard of the software development industry”. Clearly, one of us is missing something here.
It would have been nice, to be sure, had Fowler explicitly talked about how to see functors sitting inside large swaths of procedural code, but Fowler’s work was introductory within its context, and the community-at-large has written and discussed how to identify and extract functors from procedural code. In many cases, simply exposing oneself to Smalltalk, Python or Ruby was enough to make that connection.
Incidentally, object tests (the term I prefer–I agree that ‘unit’ is too vague to be useful much of the time) and removing duplication alone would be enough to discover functors. You seem to find that roundabout and weak, but I perceive it as particularly powerful, because it simplifies the model I need to understand in order extract a functor from procedural code, as you have done here.
In this case, after I’ve written the tests, then I am asked to prepend with “AAA” or “BBB”, I would remove duplication from the tests, which would point me in the direction of a Contract Test for the “map” functor. After seeing several examples of this pattern over time, I would develop the shortcut for thinking in terms of “map”. In fact, that’s essentially how I learned about map, select, detect and its cousins. What I find perhaps even more important is that I can use tests and remove duplication to discover the /next/ higher-level design concept that I don’t currently understand, or that isn’t offered by the language in which I was currently working.
So I applaud your desire to introduce your readers to functors. I only wish you’d made fewer unsubstantiated value judgments along the way. As you write, ‘I realise my statement, “denigrate the software industry” is stated without premise, but it is a statement that I stand by unless some seriously compelling evidence in favour of the contraposition comes to light.” It’s my understanding that I have the responsibility to substantiate my claims, rather than foisting the responsibility on my readers to prove the contrary. I know you didn’t ask for my advice, so forgive me, but I recommend you retain your enthusiasm while working harder to back up your negative claims.
Hi all!
I want to all of you know, World is mine, and yoursite good
G’night