Monday, 5 March 2007

Comparing closures (2 more examples) - CICE, BGGA and FCM

As my last post comparing the three closure proposals - FCM, CICE and BGGA - seemed to be useful, I thought I'd post another. Again, I'll try not to be biased!

Example 3: Sorting

The Java Collections Framework has a callback to support sorting using the Comparator interface. This is used via the two argument sort method on Collections, taking the List and the Comparator. This example will sort a list of strings by length placing the "selected" string first.

The following example is from Java 6. This uses an inner class, which can access the parameter 'selected' so long as it is final:

  public void sort(List<String> list, final String selected) {
    Collections.sort(list, new Comparator<String>() {
      public int compare(String str1, String str2) {
        if (str1.equals(selected)) {
          return -1;
        } else if (str2.equals(selected)) {
          return 1;
        } else {
          return str1.length - str2.length();
        }
      }
    });
  }

The following example is from the CICE proposal. This is shorthand for an inner class, so the parameter 'selected' must be final:

  public void sort(List<String> list, final String selected) {
    Collections.sort(list, Comparator<String>(String str1, String str2) {
      if (str1.equals(selected)) {
        return -1;
      } else if (str2.equals(selected)) {
        return 1;
      } else {
        return str1.length - str2.length();
      }
    });
  }

The following example is from the BGGA proposal, and uses the standard-invocation syntax and closure conversion. The closure can access the parameter 'selected' without it needing to be final. The closure can only return a value from its last line, and does so by not specifying the final semicolon, hence the need for the result local variable:

  public void sort(List<String> list, String selected) {
    Collections.sort(list, {String str1, String str2 =>
      int result = 0
      if (str1.equals(selected)) {
        result = -1;
      } else if (str2.equals(selected)) {
        result = 1;
      } else {
        result = str1.length - str2.length();
      }
      result
    });
  }

The BGGA proposal control-invocation syntax may not be used for this example. This is because the control-invocation syntax is not permitted to yield a result as required here.

The following example is from the FCM proposal, and uses an inner method. The inner method has access to the parameter 'selected' without it needing to be final:

  public void sort(List<String> list, String selected) {
    Collections.sort(list, #(String str1, String str2) {
      if (str1.equals(selected)) {
        return -1;
      } else if (str2.equals(selected)) {
        return 1;
      } else {
        return str1.length - str2.length();
      }
    });
  }

The example cannot be directly translated to an FCM invocable method reference. This is because the functionality being performed relies on the parameter 'selected', which is not being passed into the compareTo method on the Comparator. If the 'selected' variable was an instance variable then an invocable method reference could be used.

Example 4: Looping over a map

This example aims to print each item in a map.

The following example is from Java 6, and uses an enhanced foreach loop:

  Map<String, Integer> map = ...
  for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + "=" + value);
  }

The CICE proposal contains no new syntax to address this issue.

The following example is from the BGGA proposal, and uses the control-invocation syntax.

  Map<String, Integer> map = ...
  for eachEntry(String key, Integer value : map) {
    System.out.println(key + "=" + value);
  }

The FCM proposal contains no new syntax to address this issue. However the authors hope to publish a separate proposal to cover this use case at some point.

Summary

This blog contains two more comparisons of CICE, BGGA and FCM with examples. For the previous two, see the previous post.

6 comments:

  1. I think your FCM example needs the "final" keyword removed from the param "selected"?

    And the BGGA example could say the following:

    Collections.sort(list, {String str1, String str2 =>
    str1.equals(selected) ? -1 : str2.equals(selected) ? 1 : str1.length() - str2.length()
    });

    But you wouldn't always want to do this, and your example shows well what kinds of cases BGGA is optimized around. Specifically, giving return values for short expressions is easy. Longer blocks are best for voids. Interesting to consider, but I wouldn't call it the worst trade-off ever made.

    Thanks for the examples.

    ReplyDelete
  2. Stephen Colebourne5 March 2007 at 21:07

    @Tom, Thanks for spotting the error, its fixed now. I agree that the BGGA example could be written as you show, I just think the ternary operator is less commonly used than an if statement. Effectively, this blog shows where BGGA might be considered a little weak (example 3) or strong (example 4).

    ReplyDelete
  3. Appologies in advance for the underscores - despite what the blog says HTML syntax isn't on!

    Using C3S (http://www.artima.com/weblogs/viewpost.jsp?thread=182412) the examples are:

    __public void sort( List< String > list, String selected ) {
    ____Collections.sort list, method( str1, str2 ) {
    ______if ( str1.equals selected ) {
    ________return -1
    ______} else if ( str2.equals selected ) {
    ________return 1
    ______} else {
    ________return str1.length - str2.length
    ______}
    ____}
    __}

    and

    __final map = ...
    __for ( final entry : map.entrySet ) {
    ____System.out.println entry.getKey + "=" + entry.getValue
    __}

    ReplyDelete
  4. Stephen Colebourne7 March 2007 at 15:34

    @Howard, Thanks for the examples. I hadn't really looked at C3S until now, and wasn't really thinking of it as a closure proposal. For me, C3S is almost a different programming language - it's certainly not clear. The use of a method keyword is an interesting concept however - we just used # instead.

    ReplyDelete
  5. @Stephen,

    I was interested in your comments:


    1. A different language
    =======================

    I would say the same language, Java, with different syntax:

    1a. Short syntax for inner classes and most particularly those that have just one abstract method. The use of the keyword method is similar to Javascript, but creates an instance of an inner class.

    1b. Syntax that is based around the construct keyword ( ... ) { ... }. Like if, for, etc.

    1c. Type inference within a statement, but not between statements. Like Scala, but not as extensive as Haskel or ML.

    1d. } finishes a statement so ; isn't necessary before a }. Like end in Pascal.

    1e. () are not required for methods that have no arguments, provided that it doesn't clash with a field. Like Ruby, Groovy, etc.

    1f. () are not required for the last in a chain of top level method calls. Like Ruby.


    2. Method instead of #
    ======================

    The two constructs are similar, I favour a keyword and in particular sticking with the standard C/C++/Java constuct of keyword ( ... ) { ... }. The method # constructs are similar except that method creates an inner class. The method construct is similar to the # construct in that inner classes are extended in C3S so that final isn't necessary on local variables.

    You have mentioned in a previous blog that you see the main difference between BGGA closure and inner classes as the treatment of this. I would agree with that; but add that an inner class has two this pointers, one to the enclosing instance and one to the inheriting instance and a BGGA closure has just the this pointer to the enclosing instance. Therefore everything you can do with a BGGA closure you can do with an inner class. But tellingly the reverse isn't true, i.e. the inner class is the more powerful construct.

    ReplyDelete
  6. Howard, I find some of the ideas presented in C3S quite interesting. Actually inspiring. But I second Stephen, it looks alien wrt. Java, is in some details not clear to me, and might also cause compilation problems (e.g. using a method without parens might conflict with a field).

    ReplyDelete

Please be aware that by commenting you provide consent to associate your selected profile with your comment. Long comments or those with excessive links may be deleted by Blogger (not me!). All spam will be deleted.