The Artima Developer Community
Sponsored Link

Weblogs Forum
Extension Methods and Chained Invocations

16 replies on 2 pages. Most recent reply: Dec 19, 2007 1:21 AM by Howard Lovatt

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 16 replies on 2 pages [ 1 2 | » ]
Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Extension Methods and Chained Invocations (View in Weblogs)
Posted: Dec 13, 2007 5:11 PM
Reply to this message Reply
Summary
Two Java 7 proposed extensions, extension methods and chained invocations, give extra meaning to method call syntax. Neither proposal has proved popular with bloggers and this blog suggests an alternative.
Advertisement

First up a quick critique of the proposals.

Extension Methods

The idea is that if you use a static import then you can call an imported static method using the dot notation used currently for instance methods, e.g.:

    import static java.util.Collections.*;

    ...

    list.sort();

Whereas currently you would have to write:

    sort( list );

It is certainly nice that using the proposal you gain some readability because methods read left to right, for example consider chaining methods:

    list.synchronizedList().sort();

Which reads better than:

    sort( synchronizedList( list ) );

On the downside the proposal implies that sort is part of List and that it is dynamically dispatched. But suppose you wanted a different sort that was more optimum for a ConcurrentLinkedQueue, that was also statically imported:

    import static java.util.Collections.*;
    import static MySortMethod.*; // Includes a sort method for ConcurrentLinkedQueue

    ...
  
    List list = new ConcurrentLinkedQueue();
    list.sort();

There are two sort methods, one associated with List and one with ConcurrentLinkedQueue. With normal dynamic dispatch, as implied by the dot notation, you would expect sort( ConcurrentLinkedQueue ) to be called. But it won't be; because the dispatch is static, therefore sort( List ) is called.

Chained Invocation

This second proposal is that if a method returns void it is assumed to return its own receiver (similar to returning this but retaining the type of the receiver which might be a sub-type of this), e.g.:

    list.add( 1, "A" ).add( 2, "B" );

Currently you would write:

    list.add( 1, "A" );
    list.add( 2, "B" );

The proposal certainly reduces repetition. But it has a limited use case, e.g. add( int, Object ) returns void but add( Object ) returns boolean and therefore can't be used.

One of the main use cases for this proposal is the Builder pattern in conjunction with settable properties, e.g.:

    Home home = new Builder().setWindows( windows ).setDoors( doors ).makeHome();

I will come back to this builder example below, the key point is that the result of makeHome is a Home not a Builder.

With Clause

I would like to propose a superior alternative to both the above proposals. Pascal and other languages have a with construct that saves repeating the receiver of the call. Similarly a with construct but using an operator could be added to Java. The examples given above for both Extension Methods and Chained Invocation can be expressed as a with clause thus demonstrating that both proposals can be unified.

    list -> synchronizedList() -> sort();
    list -> { add( 1, "A" ); add( 2, "B" ); };
    Home home = new Builder() -> { setWindows( windows ); setDoors( doors ); makeHome(); };

The -> operator supplies the first argument to any method. For instance methods the first argument is the receiver (hidden this). If -> is applied to a block then the object on the left of -> is supplied to all the methods and the value of -> is the value of the last method (values from intermediate methods are discarded).

Using a different operator, ->, has the advantage of not implying dynamic dispatch and since the feature works with all methods: instance methods, statically imported functions, and non-statically imported functions it has a wide use case.

What do fellow Artima's think; are Extension Methods, Chained Invocations, or with clauses worth adding?


Aaron Maenpaa

Posts: 4
Nickname: zacherates
Registered: Nov, 2006

Re: Extension Methods and Chained Invocations Posted: Dec 14, 2007 12:04 PM
Reply to this message Reply
The example using the builder is trivially handled by current syntax:


Home home = new Builder(){{ setWindows( windows ); setDoors( doors );}}.makeHome();


However, this does introduce an anonymous subclass which may cause difficulties in some situations (Like your compiler complaining about serialVersionIds).

Axel Gross

Posts: 2
Nickname: agro
Registered: Dec, 2007

Re: Extension Methods and Chained Invocations Posted: Dec 15, 2007 10:36 AM
Reply to this message Reply
Hi Howard!

Really like your suggestion for client side extension methods.

Two thoughts:
1) don't restrict it to static imports, just use the normal namespace rules

list -> sort();

would call the method sort (no matter if local or static import) just like

sort(list);

would do

2) Different syntax as to allow nicely for qualified names
Sth along the lines of |. or :. would integrate more nicely then:

list |. sort(); // in your syntax: list -> sort()
list|.sort(); // looks more like list.sort() (i.e. normal method invocation
list |. Collections.sort(); // 'pipe' it to Collections#sort(List)
list |.synchronizedList() |.sort();


what do you think?

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 15, 2007 7:40 PM
Reply to this message Reply
@Aaron,

Yes - great use of existing syntax.

I think that one of the disadvantages of the original proposal is that it has a limited use case (void methods). Your example reinforces this view.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 15, 2007 7:44 PM
Reply to this message Reply
@Axel,

I did intend that the syntax could be used with any method (your point one). Thanks for clarifying that issue with your post.

Yes I like the syntax you propose, |., that mimics a Unix pipe. Which ever syntax people prefer, so long as it is distinct from dynamic method calls.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 15, 2007 7:58 PM
Reply to this message Reply
Having thought some more about this proposal, I think it is clearer if static method calls and dynamic method calls had different syntax - like they currently do. I am therefore now proposing that -> replaces the first formal argument, but not the receiver (hidden this), and that dynamic method calls, ., can be applied to a block like -> can. Therefore I now suggest these examples:
    list->synchronizedList()->sort();
    int index = list->{ sort(); binarySearch( key ); };
    list.{ add( 1, "A" ); add( 2, "B" ); };
    Home home = new Builder().{ setWindows( windows ); setDoors( doors ); makeHome(); };

(I stuck with my -> syntax, but Axcel's |. syntax would be good also.)

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 16, 2007 7:00 AM
Reply to this message Reply
People have to learn when to stop. I'm serious. I'm not seeing the value.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 16, 2007 1:44 PM
Reply to this message Reply
@Michael,

Valid point. Part of my reason for a response was that I thought that both Extension Methods and Chained Invocations had limited use cases and I wanted to make more general constructs. So that it was 'worth while' making the change.

Mike Kucera

Posts: 1
Nickname: mkucera
Registered: Dec, 2007

Re: Extension Methods and Chained Invocations Posted: Dec 16, 2007 5:51 PM
Reply to this message Reply
Currently its possible to call a static method on an instance of the class that contains the method. For example the following is legal Java:

String s = "hello";
String x = s.valueOf(100);

So its currently possible to use the syntax normally associated with dynamic dispatch to invoke a static method.

Fortunately Eclipse highlights the static call to valueOf() by using an italicized font. So even thought the concrete syntax can be misleading my IDE saves me from confusing it for an instance method. I can see the same thing happening with extension methods, IDEs will just hightlight them in a such way that they won't be mistaken for normal instance methods.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 16, 2007 6:13 PM
Reply to this message Reply
@Mike,

Yes - I think the IDE would be able to help. However it is nice to be able to read code outside of an IDE, e.g. on a blog in a paper. Also I think:

1. The present syntax is a mistake, it achieves nothing and is confusing

2. It is going to be very confusing to know if a built in static method or an extension method is called. And note the built in static doesn't get passed the value on the left of the dot!

Vincent O'Sullivan

Posts: 724
Nickname: vincent
Registered: Nov, 2002

Re: Extension Methods and Chained Invocations Posted: Dec 17, 2007 3:21 AM
Reply to this message Reply
> for example consider chaining methods:
>
> list.synchronizedList().sort();
>
> Which reads better than:
>
> sort( synchronizedList( list ) );
>
I'm not convinced that it does read better. In any case, the gain in readability is, at best, neglegible; whilst at the same time it introduces a notation that:
a) Duplicates existing functionality, and
b) Extends existing notation in a non-Java way (to achieve a).

It would appear to be no longer possible to tell if a called method is part of a class without examining the import structure of the calling class (or, I assume, its super classes) to see there is an applicable static import.

The gain in local reading clarity appears, to me, to be out-weighed by a more general obfuscation of the code.

> <h1>Chained Invocation</h1>
>
> his second proposal is that if a method returns
> <code>void</code> it is assumed to return its own receiver
>
> list.add( 1, "A" ).add( 2, "B" );
>
> <p>Currently you would write:</p>
>
> list.add( 1, "A" );
> list.add( 2, "B" );
>
> The proposal certainly reduces repetition. But it has a
> limited use case.

Given the limitations and the fact that all that is being saved is a return character, it seems a very minor saving that again add a (small) level of obfuscation.

> <p>One of the main use cases for this proposal is the
> Builder pattern in conjunction with settable properties,
> e.g.:</p>
>
> <pre>
> Home home = new Builder().setWindows( windows
> dows ).setDoors( doors ).makeHome();
> </pre>
>
or, using existing notation:

Home home = (new Building()).makeHome(windows, doors);

I'm fairly neutral with this change but I do think that it's a case of adding features (and therefore complexity) to the language without extending it's capabilities. The case in favour is one of making the language more concise but there appears to be only a very limited gain there.

Vince.

PS. When I previewing my text above, I see that it includes embedded links to adverts that pop up in front of the text that I'm trying to read. Is there any way to turn off these links? I realy don't like the idea that I am being forced to 'spam' readers of my text.

Eivind Eklund

Posts: 49
Nickname: eeklund2
Registered: Jan, 2006

Re: Extension Methods and Chained Invocations Posted: Dec 17, 2007 3:59 AM
Reply to this message Reply
> > for example consider chaining methods:
> >
> > list.synchronizedList().sort();
> >
> > Which reads better than:
> >
> > sort( synchronizedList( list ) );
[...]
>
> It would appear to be no longer possible to tell if a
> called method is part of a class without examining the
> import structure of the calling class

And this is a good thing. When refactoring, you don't want to mess about with client code when the change is immaterial - and whether that method is defined in an import or in the original class is immaterial to the client.

And no, refactor support in an IDE doesn't resolve this, as your version control diffs are still messed up (you DO read diffs before you commit, right? And commit one change per commit, cleanly, so you can actually do some review of what you are doing to your code, and can track back to the right message from an annotate?)


Anyway, to my mind, ideally these extra methods would be specified in the interface (as static methods, not as requirements for the receiver to implement), and the new clients would use a derived interface with more static methods instead of doing weird static imports. The call syntax would still be per the above, it just would be nicely integrated with the rest of the system. These kind of derived interfaces would automatically be implemented by all classes that implement the original interface, since all that is added is a static method or five.

Of course, that would mean that you would either have to be able to add a local "implements", saying that you see a particular class as implementing a particular interface *even if the class implementor didn't say it did*, or you would have to be able to "extract" an interface from a class definition and derive that to add your static methods.

Eivind.

Axel Gross

Posts: 2
Nickname: agro
Registered: Dec, 2007

Re: Extension Methods and Chained Invocations Posted: Dec 17, 2007 9:50 AM
Reply to this message Reply
@Vincent
the first part proposal here 'Extension Methods' uses the Java imports as they are right now in the language. So it doesn't obfuscate any method calls.
The main use would be to allow for an syntax inversion of call order (which you would use if it fits your needs) and especially allow nicer usage patterns of api's to be designed. A special symbol is introduced (e.g. "->",":-") to tell it apart from non-inverse use. It achieves the goal of giving a syntax to extension method usage and would make a special construct for server site extensions unneccessary. For client site extension methods some means of declaration or import is still needed, but I think thats orthogonal to Howard's proposal.

@myself
my original intent with ":." was to have it a little different from the use of "->" in case of qualified invocations

foo |. bar(); // versions axel_1 and axel_2
// axel_1 could be read as: pipe list to the result of FooBar#sort()
foo |. FooBar.bar(); // version axel_1
//while axel_2 pipes to FooBar and then there is some kind of invocation
foo |FooBar. bar(); // version axel_2
foo | org.stuff.FooBar .bar(); // version axel_2
foo |. org.stuff.FooBar#bar(); // version axel_3, 'method literal syntax'

I'm not sure anymore if axel_2 is any better than axel_1 though. Most probably, axel_1 would be understood, and would be much easier to parse

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Extension Methods and Chained Invocations Posted: Dec 17, 2007 6:21 PM
Reply to this message Reply
@Vincent,

I agree with Axel's point. Whether you use a static import or not is up to you with my proposal. With the original Extension Method proposal however, you have to use a static import. I have used similar syntax to this in Mathematica and it does make nested function calls easier to follow, e.g.:

list -> filter( test1 ) -> filter( test2 ) -> sort();


Reads better than:

sort( filter( filter( list, test1 ), test2 ) );


But like all proposals to extend the language the gain needs to be weighed against the pain and for a language change I think that a broad consensus needs to be reached before making changes.

Vincent O'Sullivan

Posts: 724
Nickname: vincent
Registered: Nov, 2002

Re: Extension Methods and Chained Invocations Posted: Dec 18, 2007 5:14 AM
Reply to this message Reply
> list -> filter( test1 ) -> filter( test2 ) -> sort();
>
> Reads better than:
>
> sort( filter( filter( list, test1 ), test2 ) );

Personally, I like the second format better but I'm quite willing to concede that it's because that's what I'm used to. Other than the readability gain (that some will welcome and some won't), I don't see what functionality is being added to the language by adding the new symbol "->" and a (marginally) more verbose syntax.

Is the advantage limited to losing some bracket nesting or are there further gains down the line?

Flat View: This topic has 16 replies on 2 pages [ 1  2 | » ]
Topic: Extension Methods and Chained Invocations Previous Topic   Next Topic Topic: January RIA Jam, February Flex-TurboGears Jam


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us