The notion is attractive, but then I saw some example code:
public int calcPriceInternalIterator(List items)
{
Iterator i = items.iterator();
UnaryFunction getItemPrice =
new UnaryFunction()
{
public Object evaluate (Object obj)
{
return new Double(((SETLItem)obj).getPrice());
}
};
Constant usd100 = new Constant(new Double(100));
BinaryPredicateUnaryPredicate moreThanUSD100 = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd100));
FilteredIterator fi = new FilteredIterator(i, moreThanUSD100);
Summer addPrice = new Summer();
Algorithms.foreach(fi, addPrice);
return addPrice.getSum();
}
And then in the concluding spiel:
In this article, I've introduced two functional programming techniques that can be easily integrated into your Java development practices.
... Now come on. I have no idea what that code does, because it's an ugly, bloated mess of syntax and verbose names (not to mention simply having far too many identifiers and tokens in the first place).
Java is a round hole. Functional programming is a square peg. Without modifying the syntax of the language, trying to fit the square peg in the hole may be possible, but far, far too complicated and impractical. No sane coder would want to work with that.
Isn't this really a case of Java having too much syntax rather than not enough? Or even worse, isn't this really a semantical problem? Java has rigid syntax which reflects an underlying rigid semantics. On other words, it is not merely the syntax which makes Java a poor choice for the functional programmer - it is the language design from day one.
ReplyDeleteYou're absolutely right, J.V. - the syntax of Java is too complicated (like C++, of course) and delivers too little flexibility.
ReplyDeleteI'm not sure whether to call it syntax or semantics, I guess it's a little of both, but for example, one fairly simple thing that's seriously lacking is a macro construct like the preprocessor macros in C.
These are really just syntactical constructs that can make programming much easier (programming programming, maybe that's where the term "meta-programming" comes from).
That said, at least the syntax is a bit more straightforward than C++: templates (among other things mentioned in the C++ FQA @ http://yosefk.com/c++fqa/) really screwed that up.
I agree about the initial language design being a fundamental restriction on the expressiveness of the language. It wasn't really any great leap beyond C++ (hey, no destructors or multiple inheritance, not that I ever use that last one), but somehow it seemed so new when it emerged in the 90's.
Thanks for the comment!
This is silly.
ReplyDeleteFunctional programming is like lego blocks. Java is like knex. This code sample is like using knex to build lego blocks, then trying to build a robot out of the lego blocks made out of knex.
For pete's sake, if you're going to build a robot from lego blocks, use lego blocks. If you're going to build a robot from knex, then just build the robot and don't try making them into legos first, and if you do then don't complain because it's kludgey.
Not saying there aren't functional programming ideas that can be adapted in Java. Just saying, some common sense needs to be applied.
anonymous, that's what I was getting at in my original post.
ReplyDeleteWhat's funny is the author presented the code and implies that it's an easy way to integrate those techniques with Java.
I don't think it's easy, and clearly not easy the way he did it. More like a nightmare :)
The whole point of using a dedicated functional language like Haskell is that you can write stuff like:
map (*5) [1,2,3]
Rather than having to make some anonymous subclass like "new UnaryFunction() { public Object evaluate(Object obj) { return new Integer(((Integer)obj)*5); } }" to take the place of "(*5)". That _is_ just silly.
Took me a while to decipher that, but the equivalent Scala code looks something like:
ReplyDeleteitems.filter(_.price >= 100).foldLeft(0d)(_ + _)
You can also shorten it down to:
items.filter(_.price >= 100).sum
Provided you've added a "sum" method on Iterable[Double] with something like:
implicit def IterableWithSum(iter: Iterable[Double]) = new { def sum = iter.foldLeft(0d)(_ + _) }
Thanks Jorge! I like the look of Scala - even if it's a little slower than vanilla Java, the syntax is nicer.
ReplyDeleteThe facility for pattern-matching function definitions is cool.
I'm no fan of Java but focusing on the concept and subsequent reliability aspect of some of the functional ideas is fairly useful. So whatever language you use, if you work up a blob that reacts in a consistent manner and perhaps with hooks demonstrating it is well tested, thats a good thing to have.
ReplyDeleteIndeed, but maintaining/understanding that blob at a later time is more common than you expect when you start coding, and badly hampered if the code looks like this ;)
ReplyDeleteYou said "Java is a round hole."
ReplyDeleteWell put.
Even without pulling out the big functional programming language guns, we end up with this in C# 3.0 (both Where and Sum are built in):
ReplyDeleteitems.Where(i => i.getItemprice() >= 100).Sum()
Yeah, I heard C# 3.0 came with quite a few functional programming features (although no way to eliminate or constrain side-effects?) which is encouraging.
ReplyDeleteAnd now with F# - perhaps that will become a less kludgy dialect of Ocaml?
C# 3.0 permits side effects (as do F# and OCaml). F# has access to the .NET Framework library, much of which could be considered less kludgy than OCaml's libraries. It really depends on what you're doing (regex comes to mind).
ReplyDeleteI'm sure there's a cleaner solution in F#, but this accomplishes the task:
items |> map (fun i -> i.getItemPrice()) |> filter (fun p -> p >= 100) |> fold_left (+) 0
I also noticed putting this together that I missed a map() in the C# example I adapted from Jorge's post. There are a few ways of writing it in C# with the necessary map included:
items.Select(i => i.getItemPrice()).Where(p => p >= 100).Sum();
items.Where(i => i.getItemPrice() >= 100).Sum(i => i.getItemPrice())
(from i in items let price = i.getItemPrice() where price >= 100 select price).Sum()
Thanks Derek. I have to say, I never really gave C# a proper look, partly because of an old "but it's Microsoft" prejudice.
ReplyDeleteBut with some of the expressiveness of functional programming techniques built in now, I think it deserves a proper try.
A good deal of the programmers, if not all, for their first 3-5 years, are very good (this included me, and still does on some topics). One of the idea behind java, wich made it both simpler and more cumbersome, depending on the aspects, was to remove error prone concepts. I still think it is a good idea, though it could have evolved into a sort of -programmerLevel=junior,average,senior compiler option allowing to use or not user certain construct. It is easier with syntax than with guns, it you see wich part of the US constitution I'm talking about.
ReplyDeleteBut I'm definitely missing first class function, delegate keywords and operators, so I hope they find their way in java. In the meantime I learned to type with all fingers, which more or less solved the verbosity program. Everyday programming is not (only) having a good language, but also revolves around stupid things. After all languages are just tools to make programs.
I recently wrote an article addressing the issues of readable functional programming in java: http://community.livejournal.com/jkff_en/341.html ; you might be interested to have a look.
ReplyDeleteAfterwards, I stumbled upon the article you are discussing and actually laughed out loud because it contradicts each and every 'readability rule' discussed in my article, but one can forgive the guy because he wrote the article when there were no generics, no static imports and no varargs - there was not much he could do..
Thanks antilamer; I didn't even know Java supported varargs now, and I hadn't really thought about the utility of static imports. You still have to stick methods in a class, but with a static import, you don't have to mention the classname every time.
ReplyDeleteThe only times I've used it so far are with JUnit's assert* methods, but your post on functional-like abstractions is an eye-opener.
Nice one!
> I don't think it's easy, and
ReplyDelete> clearly not easy the way he did
> it. More like a nightmare :)
Yes it is easy. No, not the way he did it.
Some examples...
public class Test {
public static void main(String[] args) {
FInteger i = new FInteger(4);
// prints:
// hi 1
// hi 2 ... etc.
i.times(new Closure() { public void execute(Object o) { System.out.println("hi " + o); }});
FString s = new FString("ABCDEFG");
// prints:
// hi A
// hi B ... etc.
s.each(new Closure() { public void execute(Object o) { System.out.println("hi " + o); }});
// prints "true" if ALL characters in string 's' are less than D
System.out.println(s.forAll(new Closure() { public boolean holds(Object o) { return ((Character)o).charValue() < 'D'; }}));
// prints "true" if ANY character in string 's' is less than D
System.out.println(s.exists(new Closure() { public boolean holds(Object o) { return ((Character)o).charValue() < 'D'; }}));
}
}
Yes, some classes have been extended/augmented. If you want details of that (very simple) code, I am happy to send and/or po
st.
Hi anon,
ReplyDeletePlease do post the code; this looks much closer to the functional programming one could live with.
From the fact that you have one Closure subclass which can have arbitrarily-named/parameterised methods, I assume you're using reflection - i.e. FString.exists(Closure c) does something like 'c.getClass().getMethod("holds").invoke(...)'?
I've heard a reflective method call is (was?) 100 times slower in Java than a 'straight' call, but that was a few years ago. It's surely worth it for the readability gain, anyway.