Path: csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!aioe.org!news.glorb.com!news.netfront.net!not-for-mail From: Wanja Gayk Newsgroups: comp.lang.java.programmer Subject: Re: Style Police (a rant) Date: Sun, 11 Sep 2011 21:20:24 +0200 Organization: Netfront http://www.netfront.net/ Lines: 222 Message-ID: References: NNTP-Posting-Host: 77.8.106.205 Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: adenine.netfront.net 1315768822 87123 77.8.106.205 (11 Sep 2011 19:20:22 GMT) X-Complaints-To: news@netfront.net NNTP-Posting-Date: Sun, 11 Sep 2011 19:20:22 +0000 (UTC) User-Agent: MicroPlanet-Gravity/3.0.4 Xref: x330-a1.tempe.blueboxinc.net comp.lang.java.programmer:7816 In article , avl@gamma.logic.tuwien.ac.at says... > > Wanja's main point is that 'final' has a semantic purpose, not an > > optimization one. > > So far I agree. Well understood. > > I agree that the evidence for the advantage of a 'var' keyword > > over a 'final' is lacking, > > That Wanja suggested the "var" keyword is an indication of that he has > a bias towards functional programming. Hm, I can't really say that this is the case. It's just in the past few years that I'm adopting rather functional paradigms, which in Java mostly boils down to using the strategy pattern and a preference towards methods that are free of side-effects. But using "finally" a lot has hardly anything to do with functional programming in my opinion. I'm doing it for the sake of precision of expression (don't declare something variable if it's not) and staying focussed. Anything that is not "final" immediately draws my attention for being a subject of change, which means I can forget about most of the variables when analyzing code. This is also why I narrow the scope of variables a lot. Here's an example: I have seen code that looks like this: //O(n*n) runtime, but "in place", suitable only if //memory is far more important than runtime. // //[][1,2,3,4,3,2,1] head contains 1 ? -> false //[1][2,3,4,3,2,1] head contains 2 ? -> false //[1,2][3,4,3,2,1] head contains 3 ? -> false //[1,2,3][4,3,2,1] head contains 4 ? -> false //[1,2,3,4][3,2,1] head contains 3 ? -> true //[1,2,3,4][2,1] head contains 2 ? -> true //[1,2,3,4][1] head contains 1 ? -> true //[1,2,3,4][] public void withoutDupes(Iterable iterable) { Iterator tail = iterable.iterator(); int tailIndex = -1; int headIndex = 0; Iterator head = null; T head ofTail = null; T objectInHead = null; while (tail.hasNext()) { tailIndex++; headOfTail = tail.next(); headIndex = 0; head = iterable.iterator(); while (headIndex++ < tailIndex) { objectInHead = head.next(); if (matches(headOfTail, objectInHead)) { tail.remove(); tailIndex = tailIndex-1; break; } } } } I find this unnecessary hard to follow, because I have a hard time to see which value changes when and where a value is needed. I first start to reduce the scope of the variables, which also kills unnecessary initializations: public void withoutDupes(Iterable iterable) { Iterator tail = iterable.iterator(); int tailIndex = -1; while (tail.hasNext()) { tailIndex++; T headOfTail = tail.next(); int headIndex = 0; Iterator head = iterable.iterator(); while (headIndex++ < tailIndex) { T objectInHead = head.next(); if (matches(headOfTail, objectInHead)) { tail.remove(); tailIndex = tailIndex-1; break; } } } } Then I would add the "finally"-keyword to any place appropriate, Ecipses source-"clean up" function does that for me, so it's just three or four few mouseclicks/keystrokes away: public void withoutDupes(final Iterable iterable) { final Iterator tail = iterable.iterator(); int tailIndex = -1; while (tail.hasNext()) { tailIndex++; final T headOfTail = tail.next(); int headIndex = 0; final Iterator head = iterable.iterator(); while (headIndex++ < tailIndex) { final T objectInHead = head.next(); if (matches(headOfTail, objectInHead)) { tail.remove(); tailIndex = tailIndex-1; break; } } } } At this point I find the code a lot easier to follow, because I have less variables to trace and their scope is limited. It looks even better in the IDE, because of the syntax highlighting. The variables "tailIndex" and "headIndex" immediately catch my attention, they are not preceeded with "finally" so those are the running variables and as such the most interesting when tracng the code. Still that would not satisfy me. As a final touch I'd change the increments where appropriate to clarify the order of evaluation, because "if(i++ < 10){...}" is short for "int temp = i; i=i+1; if(temp < 10){..}" which is unnecessarily complicated. So as I would end up with: public void withoutDupes(final Iterable iterable) { Iterator tail = iterable.iterator(); int tailIndex = -1; while (tail.hasNext()) { ++tailIndex; T headOfTail = tail.next(); int headIndex = -1; Iterator head = iterable.iterator(); while (++headIndex < tailIndex) { T objectInHead = head.next(); if (matches(headOfTail, objectInHead)) { tail.remove(); --tailIndex; break; } } } } Now this is a code I find a lot easier to understand than the first version, that looked like this: public void withoutDupes(Iterable iterable) { Iterator tail = iterable.iterator(); int tailIndex = -1; int headIndex = 0; Iterator head = null; T head ofTail = null; T objectInHead = null; while (tail.hasNext()) { tailIndex++; headOfTail = tail.next(); headIndex = 0; head = iterable.iterator(); while (headIndex++ < tailIndex) { objectInHead = head.next(); if (matches(headOfTail, objectInHead)) { tail.remove(); tailIndex = tailIndex-1; break; } } } } Still I could refactor out one or two methods, but at this time I would probably stop and call it a day. > Unlike Arne, I don't say that > "pick a different language" is an appropriate answer. It's ok, to program > functionally in Java, as far as possible in Java-syntax, and that this > may lead to quite a lot of "final"s. > I just disagree with Wanja as to making "final" the default: it's > like bullying others to functional style in Java. The above code is by no means functional, is it? Functional code would rather look like this: public List withoutDupes(final List xs) { return new Object() { List withoutDupes(final List head, final List tail) { if(tail.isEmpty()){return head;} if (head.contains(tail.get(0))) { return withoutDupes(head, tail.subList(1, tail.size())); } return withoutDupes( new ArrayList(head){{add(tail.get(0));}} , tail.subList(1, tail.size()) ); } }.withoutDupes(Collections.emptyList(), xs); } This is a whole different beast (and prone to crash with a stack overflow exception on larger lists by the way). Admitted, it is not entirely functional due to the "add"-call, but quite close. Still it is pretty compact code (there is a certain beauty in recursion, isn't it?) and not hard to understand either. Bullying someone to functional code would be pretty stupid, as the current JVMs still have a hard time detecting tail recursions and it lacks data structures that do lazy evaluation in the Java SE. Kind regards, Wanja -- ..Alesi's problem was that the back of the car was jumping up and down dangerously - and I can assure you from having been teammate to Jean Alesi and knowing what kind of cars that he can pull up with, when Jean Alesi says that a car is dangerous - it is. [Jonathan Palmer] --- Posted via news://freenews.netfront.net/ - Complaints to news@netfront.net ---