The Artima Developer Community
Sponsored Link

Weblogs Forum
Lexical Scope of Java Inner Classes and Closures

23 replies on 2 pages. Most recent reply: Jun 24, 2007 4:51 AM by Jules Jacobs

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 23 replies on 2 pages [ « | 1 2 ]
Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 14, 2007 7:49 AM
Reply to this message Reply
Advertisement
That snippet is very confusing indeed :)

I don't think it's like callcc though. You just hardcoded the loop into callcc (so it supports looping). You cannot use it to return twice. The idea is that you return from the callcc block twice, not that you can do crazy things inside the callcc block.

example:

def foo
  callcc do |cc| 
    $k = cc # set global variable
    return 1
  end
  return 4
end
 
print foo() # 1
$k.call() # 4, 4, 4, 4, 4 (goes back to the previous line and returns from foo() again. The value returned from foo is passed to print() again)

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 14, 2007 2:06 PM
Reply to this message Reply
The example would be:

    Callcc< Integer >.new {
        method iteration { value = ( value == null ) ? 1 : ( value + 1 ) }
        method repeatable {
          out.println value;
          if ( value < 3 ) { callccContinue }
        }
      }.call();


The changes are:

1. The Callcc is scoped over the whole construct, eliminating k

2. Function foo is replaced by field value, where value acts as the value to be returned inside iteration and as the value returned from the previous execution inside both iteration and repeatable. Field value is initially null.

3. The loop terminates

The termination is interesting because value (foo) can be tested before value (foo) is called again (via callccContinue). The equivalent terminating code code in Ruby/Scheme style would require an extra variable.

The infinite-loop version, i.e. as originally written, would be:

    Callcc< Integer >.new {
        method iteration { value = ( value == null ) ? 1 : 4 }
        method repeatable {
          out.println value;
          callccContinue
        }
      }.call();

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 14, 2007 2:46 PM
Reply to this message Reply
Yes, of course you can translate callcc examples to Java: Java's turing complete. The point is that it requires global code restructuring.

As an exercise, try to implement amb using your callcc. Amb is the angelic operator, and can be easily implemented using continuations. It works like this:

a = choose(1, 2, 3, 4, 5, 6)
b = choose(1, 2, 3, 4, 5, 6)
 
assert(a + b == 7) # try other numbers if a + b != 7
 
print a, b # will only print a's and b's that sum to 7
 
assert(false) # next possibility


Amb arranges the variables so that tests do not fail. This code will print all numbers from 1 to 6 that sum to 7: 1+6, 2+5, 3+4, 4+3, 5+2, 6+1. Yes this is magic.

Yes you can do this with a simple loop, but it will get real hairy for more complex examples, especially if amb is used as the return value of a function:

def foo(x)
  a = choose(*(0..x))
  b = choose(*(0..x))
  assert(a + b == 7)
  return [a,b]
end
 
a, b = foo(6)
 
print a, b
 
assert(false) # next one


You need to inline foo (global restructuring) if you want to change this to a loop.

Another example:

def only_primes(x)
  assert(x.prime?)
end
 
def only_pythagorean_triples(a, b, c)
  assert(a**2 + b**2 == c**2)
end
 
def three_numbers_upto(x)
  return [choose(*(0..x)), choose(*(0..x)), choose(*(0..x))]
end
 
a, b, c = three_numbers_upto(100)
 
only_primes(a) # try again if a isn't prime
 
only_pythagorean_triples(a, b, c) # try again if a, b, c is not a pythagorean triple
 
print a, b, c # here we have a pythagorean triple a, b, c, and a is prime.


Can you see the very high level abstraction? This abstraction is lost if you have to inline all functions and put them in a loop.

Here's a Ruby amb: http://www.rubyquiz.com/quiz70.html

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 16, 2007 9:36 PM
Reply to this message Reply
I think you can write Amb from Callcc in standard Java:

public class Amb< R, A > extends Callcc< R > {
  public final Method0< A >[] methods;
  
  private int index = 0;
  
  public Amb( final Method0< A >... methods ) { this.methods = methods; }
  
  public final A next() throws AmbException {
    while ( index < methods.length ) { 
      try {
        return methods[ index++ ].call();
      } finally {
        // Try next method
      }
    }
    throw new AmbException();
  }
  
  public final void fail() { callccContinue(); }
  
  @Override public String toString() { return "Amb[" + value + "]"; }
}


The if example from http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14 becomes in C3S:

    final angelic = Amb< Integer, Boolean >.new( method { false }, method { true } ) {
        method repeatable {
          if ( next ) { value = 1 }
          else { fail }
        }
      }.call;


A search example in C3S might be:

    final range = Method0< Integer >.new[] { method { 0 }, method { 1 }, method { 2 }, method { 3 } };
    final outer = t (Amb< Integer, Integer >)null; // Forward declaration, t makes a tuple
    outer.e1 = Amb< Integer, Integer >( range ).new {
        method repeatable {
          value = next;
          final inner = Amb< Integer, Integer >.new( range ) {
              method repeatable {
                value = next;
                if ( outer.e1.value + value == 3 ) { out.println "Found: " + outer.e1 + " + " + this + " = 3" }
                fail; // Repeat inner loop
              }
            };
          try {
            inner.call; // Start inner loop
          } catch ( AmbException notUsedInner ) {
            // End inner loop
          }
          fail; // Repeat outer loop
        }
      };
    try {
      outer.e1.call; // Start outer loop
    } catch ( AmbException notUsedOuter ) {
      // End outer loop
    }

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 20, 2007 12:51 AM
Reply to this message Reply
Yes, you can do that, but that isn't amb. Can you write a method that returns 1, 2 and 3 like this:

def foo
  x = choose(1,2,3)
  return x
end

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 20, 2007 1:53 PM
Reply to this message Reply
Hi Jules,

I agree with you that it is never identical to a functional language because it is object oriented not function oriented. But by sumbstituting an object you can get the same behaviour, e.g.:
static final x = Choose.new( 1, 2, 3 );
method Integer foo { x.next }

Assuming that you derive Choose from Amb.

Also in my posted version of Amb I didn't chain the fail of an inner Amb to the fail of an outer Amb. You can do this chaining by passing in a chain argument to the constructor and if one isn't passed in then use reflection to find enclosing classes and if any of these are an Amb then automatically chain to it. The Scheme version of Amb chains automatically.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 21, 2007 12:54 AM
Reply to this message Reply
Of course the syntax will be different, but that isn't a problem. The foo I posted will do this:

x = foo()
y = foo()
 
print x, y
 
fail


This will print

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

Your foo will not, unless you wrap it in a loop or in an exception handler or something. You have to add some extra bookkeeping & control flow every time you use foo in Java. The point of continuations is that this bookkeeping can be hidden / abstracted over in a method/object.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 22, 2007 7:08 PM
Reply to this message Reply
Sure - to do what you want you need an instance of a class. In C3S Java something like:
    abstract class Foo extends Choose< Character > {
      new { super 'a', 'b' }
    }
    class X extends Foo {
      method {
        Foo.method {
            out.println "Foos: " + X.this.value + " and " + value;
            fail X.this // Repeat all combinations
          }.startChooseLoop // Start inner loop
      }
    }
    X.new.startChooseLoop // Start outer loop
  }

Not only is the method changed to a class and the call to an instance of the class, but also the scope is explicit. Personally I like the explicit scoping since I think the code is easier to follow, you don't need to remember which methods use callcc.

Of cause in Java you would almost certainly use a loop, which I think most people would prefer, and that has explicit scope. The examples are just to show what you can do; the examples may have some value if you are translating code, but I doubt it :)

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Lexical Scope of Java Inner Classes and Closures Posted: Jun 24, 2007 4:51 AM
Reply to this message Reply
I think you misunderstand amb. The point *is* the abstraction: you don't have to code an explicit loop.

Your example is also wrong: foo should be encapsulated somewhere and called somewhere else. In this somewhere else place there shouldn't be a loop in sight.

We're printing the number in the example I gave, but we could also have put them in an array like this:

a = [foo(), foo()]


Which produces arrays like this:

[1,1]
[1,2]
...
[3,3]


Or define a function like this:

def bar()
  (1..foo()).map{foo()}
end


Can you figure out what this does? It's hard if you don't see the abstraction. Can you translate this into Java?

[1]
[2]
[3]
[1,1]
[1,2]
...
[3,3]
[1,1,1]
[1,1,2]
...
[3,3,3]


Here's my amb lib and an example (coloring the map of western europe):

# amb lib
 
$fail = lambda { fail "No solution found." }
 
def choose(*choices)
  prev_fail = $fail
 
  callcc do |ret|
    for choice in choices
      callcc do |next|
        $fail = next
        ret.call(choice)
      end
    end
 
    prev_fail.call()
  end
end
 
def assert(x)
  choose if !x
end
 
# end of amb lib
 
def country
  choose(:red, :blue, :yellow, :green)
end
 
def adjacent(country1, country2)
  assert(country1 != country2)
end
 
netherlands = country
belgium = country
france = country
spain = country
portugal = country
germany = country
luxemburg = country
switzerland = country
austria = country
italy = country
 
adjacent(netherlands, belgium)
adjacent(netherlands, germany)
adjacent(belgium, france)
adjacent(belgium, germany)
adjacent(belgium, luxemburg)
adjacent(france, spain)
adjacent(france, germany)
adjacent(france, italy)
adjacent(france, luxemburg)
adjacent(france, switzerland)
adjacent(spain, portugal)
adjacent(germany, switzerland)
adjacent(germany, luxemburg)
adjacent(germany, austria)
adjacent(switzerland, austria)
adjacent(switzerland, italy)
adjacent(austria, italy)
 
puts "netherlands: #{netherlands}"
puts "belgium: #{belgium}"
puts "france: #{france}"
puts "spain: #{spain}"
puts "portugal: #{portugal}"
puts "germany: #{germany}"
puts "luxemburg: #{luxemburg}"
puts "switzerland: #{switzerland}"
puts "austria: #{austria}"
puts "italy: #{italy}"


This outputs:

netherlands: red
belgium: blue
france: red
spain: blue
portugal: red
germany: yellow
luxemburg: green
switzerland: blue
austria: red
italy: yellow


Which is correct I suppose, but I haven't checked this answer.

I think this is very cool: the code is extremely high level compared to deeply nested loops. I coded this amb lib and the problem spec in a few minutes. The code is the spec. This isn't possible without continuations.

Flat View: This topic has 23 replies on 2 pages [ « | 1  2 ]
Topic: Lexical Scope of Java Inner Classes and Closures Previous Topic   Next Topic Topic: Appreciative Inquiry

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use