5 ms·
> Do not use loops for list operations. I can not get behind this in Java if the filter, fold, or map operation is simply a one-off. (It's another matter entir
by WilliamLP 17y ago
> Do not use loops for list operations.
I can not get behind this in Java if the filter, fold, or map operation is simply a one-off. (It's another matter entirely though if the power of the functional approach is useful in the situation, say if a filter should be switched dynamically.) To do this for something static though, such as eliminating empty strings or adding an array, is just worse than useless.
This is as clear an example as any of programming against the language, not into it. The code is longer, it is slower, and _for a Java programmer_ it is much less clear. Java was explicitly designed not to be a language for showing off cute constructs for their own sake.
- mynameishere 17y agoI don't understand google's awful implementations. In my code by contrast, a map function looks like this: public double foo(double x) { return x*x; } list=Utility.map(this,"foo",list);
- jimbokun 17y agoYou need to do reflection, which probably has performance implications. You also cannot do closures, which you can simulate with anonymous inner classes. Might be worth the trade off in your case, but maybe not for the cases Google has in mind.
- brunomlopes 17y agoNot only does that require reflection, it is very error prone and not refactor-friendly. Just change the type of the list, change the method, and it will blow up on runtime instead of compile time. In my opinion, that goes against the grain of Java (and c#, in a way) of having the compiler doing a lot of checking for you. Google's approach, while more verbose, is a bit safer and more idiomatic.
- bfung 17y agoIn addition to the other comments, the Utility class used in this example defeats the purpose of OO programming that Java is more in tune with. What's to stop the proliferation of these Util classes with static methods? I believe the more Java way would be to extend the List interface to add a map method and implement a corresponding class. To make this less of a pain, extend from one of the JDK provided classes if possible... but yes, it's still a pain.
- jimbokun 17y agoIt could be worthwhile if this proposal were implemented: http://docs.google.com/Doc.aspx?id=k73_1ggr36h http://docs.google.com/Doc.aspx?id=k73_1ggr36h "Here is the same code rewritten using the proposed syntax:" List<String> ls = ... ; Collections.sort(ls, Comparator<String>(String s1, String s2){ return s1.length() - s2.length(); }); By making anonymous inner class declaration more concise, you get many of the benefits of closures, without changing Java semantics.
- barrkel 17y agoI agree, but unlike the other commenter, I think some equivalent of LINQ or concise (i.e. using type inference) syntax for lambdas is necessary to really make it work. And even then, for imperative operations that need to be applied to a subset of elements of a list, an imperative for-loop is still usually the best way to do it.
- jimbokun 17y agoAnother thought: Why doesn't Java make it easier to refer to a method and call it dynamically? For this example: List<Person> beerDrinkers = new ArrayList<Person>(); for (Person p: persons) { if (p.getAge() > 16) { beerDrinkers.add(p); } } If we had a legalAge method defined on Person returning true for any person over 16, then it would be nice to write the equivalent of List<Person> beerDrinkers = filter(persons, legalAge); which would call the legalAge method on each person and keep the ones returning true. Like most things with Java, you can do this, kind of, but it means getting the instance class, looking up the method for the method name and signature, then invoking on the instance, all of which makes it just not worth it. Objective C has the idea of selectors, which is an easy way to specify the message you want to send to an object, which probably comes from some similar concept in SmallTalk. This is arguably a more object oriented solution to this general problem. For one, it better follows Demeter's Law, as you do not need to expose the getAge property.
- scott_s 17y agoIn C++, the way you would do this involves boost::men_fn (http://www.boost.org/doc/libs/1_39_0/libs/bind/mem_fn.html http://www.boost.org/doc/libs/1_39_0/libs/bind/mem_fn.html): list<Person> beerDrinkers = filter(persons, boost::mem_fn(&Person::legalAge)); Where filter looks something like: template <class Sequence, class Pred> Sequence filter(const Sequence& in, Pred p) { Sequence out; for (typename Sequence::const_iterator i = in.begin(); i != in.end(); ++i) { if (p(*i)) { out.insert(out.end(), *i); } } return out; } We can even use this style if legalAge is something that takes a parameter, like ageCheck(age) using boost::bind (http://www.boost.org/doc/libs/1_39_0/libs/bind/bind.html http://www.boost.org/doc/libs/1_39_0/libs/bind/bind.html): list<Person> beerDrinkers = filter(persons, boost::bind(&Person::ageCheck, 21, _1));
- jsankey 17y agoI disagree. The fact that it is slower has no practical impact in the vast majority of cases, and is certainly not nearly as important as readability. So it comes down to which is clearer, and I find the functional style - even with the extra ceremony - more clearly conveys the code's intent. No messing around with the how, just show me the what. It's not about showing off, it's about abstraction of extremely common idioms. Pointing this out as unclear for a "Java programmer" is a bit of a red herring I think. Anyone can learn what map/filter/reduce/etc mean in no time at all -- it seems a simple task compared to learning the project-specific abstractions of any codebase.
- WilliamLP 17y ago> Pointing this out as unclear for a "Java programmer" is a bit of a red herring I think. I think it's really not. There's a talk out there, I think by Gosling, about the direction of Java including why lambdas were rejected for Java 7. One reason was actually to prevent the sort of functional code that polarizes people in terms of whether it is seen as beautiful or opaque. (E.g. currying, Y combinators.) Java provides the flexibility to use functional programming through anonymous inner classes, but at considerable pain to the author. The point people miss is that that pain is actually a conscious part of the language design. Anyone can learn about map/filter/reduce, and every programmer needs to for the situations when they are crucial. But on the other hand, the problems with explicit looping are much overstated and I'll admit to emotionally siding on the issue with the creators of Java and Python. I don't think you can ever fully get the "how" out of programming, nor should you try, nor is "what" implicitly superior all the time in all situations.
- jsankey 17y ago> The point people miss is that that pain is actually a conscious part of the language design. That may be true, but it doesn't make it the right choice. And AFAIK Gosling himself has always been pro-closures, to remove this pain (see http://blogs.sun.com/jag/entry/closures http://blogs.sun.com/jag/entry/closures). >I'll admit to emotionally siding on the issue with the creators of Java and Python The big difference in Python is that there are "pythonic" alternatives for the really common cases. > nor is "what" implicitly superior all the time in all situations. I agree there is such a thing as too much abstraction, but I really don't think map/filter/etc fall into this category. These operations are so frequent that if you don't abstract the how, you end up repeating it thousands of times in a significant codebase. At that point I think you end up on the wrong side of the fence.
- darkxanthos 17y agoThe code is longer: Without having something like extension methods this might be the case. I'm a C# dev so I'll partially concede here due to ignorance. That predicate syntax really does suck... it'd be nice if you had lambdas but [shrugs] The code is slower: It is slower but in my experience it is very rare that our performance bottle necks occur in these sections of code. We have actually profiled our code and changed from for loops in the legacy version to a redesigned version of the class using this more functional approach during refactoring and we can meet our performance needs just fine. More often than not the big performance improvements lie in disk i/o or more intelligent algorithms rather than just a general use of more functional idioms. Much less clear _for a Java programmer_: Isn't that always the case until programmers start to learn other ways of doing things? If this was just one guy and not what appears to be a industry wide sea-change maybe I wouldn't argue. The functional paradigm however is being given a fair amount of attention and I don't think it's something you can classify as a niche concern. "cute constructs for their own sake"- I believe the point of developers moving away from loops is that we are starting to realize that explicitly asking the compiler to loop through these items in this particular order is over-specification 99% of the time. Really we are just saying do this to each one of these. By shifting to a functional paradigm these types of tasks can be easily parallelized and abstracted to the point that you really care about.
- deleted 17y ago[deleted]
- swolchok 17y agoHow much power is wasted worldwide by slow code that isn't the bottleneck? Surely, fast habits are better than slow habits, all else being equal.
- darkxanthos 17y agoWhat kind of question is that? Of course fast > slow where all else is equal, the problem is that that isn't the case. I made more than one point regarding the use of FP style where applicable and there's others as well (including maintainability which is a huge money hole). Whether or not you think the performance implications are worth the benefits is a personal, contextual decision. But you just grossly misrepresented the entire point by saying that both styles are equivalent except that one is slower.