The Artima Developer Community
Sponsored Link

Weblogs Forum
An Alternative to Closure Conversion and to Restricted Closures

30 replies on 3 pages. Most recent reply: Jan 13, 2008 2:12 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 30 replies on 3 pages [ « | 1 2 3 | » ]
Elizabeth Wiethoff

Posts: 89
Nickname: ewiethoff
Registered: Mar, 2005

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 1:42 AM
Reply to this message Reply
Advertisement
> Thanks, I was trying to respond to Charles H. I was
> unaware that posts are flat and do not nest.

There's a "Threaded View" link at the upper right of the page, but it's not the default view.

Elizabeth Wiethoff

Posts: 89
Nickname: ewiethoff
Registered: Mar, 2005

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 1:44 AM
Reply to this message Reply
> Thanks, I was trying to respond to Charles H. I was
> unaware that posts are flat and do not nest.

There's a "Threaded View" link at the upper right of the page, which seems to indent the threads, but I almost never use it. I prefer the default "Flat View."

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 9:01 AM
Reply to this message Reply
The title of this blog post is broken. Closure conversion is a compilation technique that some functional language compilers use, totally not what your are describing.

Also, the "closure conversion" you are describing feels like an awful hack.

Furthermore you are confusing "closure" and "anonymous function".

Lastly, the solution is quite obvious. return returns from the enclosing method, and if you don't use a return statement then the value of the expression is returned.

int foo(){
  {x => return x}.invoke(10); // anonymous function has type int -> void.
  bar(); // will not be executed!
}
 
===
 
int foo(){
  return {x => x}.invoke(10) // anonymous function has type int -> int.
  bar(); // will not be executed!
}


For the 0.01% of the cases where you explicitly want to return from an anonymous function you can use exceptions.

Neal Gafter

Posts: 12
Nickname: gafter
Registered: Oct, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 11:01 AM
Reply to this message Reply
> @Howard:
> You wrote: "As the spec. notes Function0 is synthesized by
> the compiler, therefore you know Function0 is closure
> therefore you could allow further boxing to an arbitrary
> interface, just like you do for a closure literal"

You are confusing function type interfaces with closures. There is nothing in the spec that requires that variables of function types contain closures. Since a function type is an ordinary interface type, you can create a value of a function type just by implementing the interface, or (if you're masochistic) by writing an anonymous inner class.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 12:14 PM
Reply to this message Reply
> The title of this blog post is broken.

I am using closure conversion as used by Neal Gafter et al. in their language proposal: http://www.javac.info/closures-v05.html

> Also, the "closure conversion" you are describing feels
> like an awful hack.

I am trying to make it less of a hack by proposing that it behaves more like an inner class

> Furthermore you are confusing "closure" and "anonymous
> function".

See reply to Neal's post - after this one

> Lastly, the solution is quite obvious. return
> returns from the enclosing method, and if you don't use a
> return statement then the value of the expression is
> returned.

That works for some cases, but what about multiple threads, what about storing the closures in a data structure, what about:

static { => void } makeMethod() {
return { =>
...
return { => ...; }; // Error not caught by the compiler! Returns from makeMethod and the closure
};
}

static void main( String[] notUsed ) { makeMethod().invoke(); }

The correct code for the above example is:

static { => void } makeMethod() {
return { =>
...
{ => ...; } // Error not caught by the compiler! Returns from makeMethod and the closure
};
}

But I think it will be common mistake to put return in because you use return everywhere in Java.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 12:44 PM
Reply to this message Reply
@Neal:

> You are confusing function type interfaces with closures.
> There is nothing in the spec that requires that variables
> of function types contain closures.

Yes I am, I am surprised that you have chosen to make such a distinction. You could say that a function type can only be assigned to from a closure. The disadvantage of the aproach you have chosen is that the following is confusing:

executor.execute( { => ...; } ); // OK


But

{ => void } toBeRun = { => ...; };
executor.execute( toBeRun ); // Oops


The problem I foresee is that the syntax for a function type is so similar to the syntax for a closure that other people, like myself, will be surprised that the two aren't interchangeable and you could choose to make them interchangeable. Much as a literal 1 (analogous to a closure literal) is interchangeable with an int (analogous to a function type).

A further reason to make function types and closures one in the same, is that as your proposal stands (but not how I suggested changing it in this blog) you need function types for recursion:

{ int => int } factorial = { x => x <= 1 ? 1 : x * factorial( x - 1 ) };

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 1:42 PM
Reply to this message Reply
> I am using closure conversion as used by Neal Gafter et
> al. in their language proposal:
> http://www.javac.info/closures-v05.html
> [...]
> See reply to Neal's post - after this one

I see. The term is already in use: http://en.wikipedia.org/wiki/Closure_conversion so if Neal wants to prevent confusion he has to choose another name.

> I am trying to make it less of a hack by proposing that it
> behaves more like an inner class

That's great, but the feature of converting an anonymous function to a class with one method automatically "renames" the invoke method is still there, right? This isn't going to interact well with inheritance.

> That works for some cases, but what about multiple
> threads, what about storing the closures in a data
> structure, what about:
>
> static { => void } makeMethod() {
> return { =>
> ...
> return { => ...; }; // Error not caught by the
> the compiler! Returns from makeMethod and the closure
> };
> }
>
> static void main( String[] notUsed ) {
> makeMethod().invoke(); }
>
> The correct code for the above example is:
>
> static { => void } makeMethod() {
> return { =>
> ...
> { => ...; } // Error not caught by the compiler!
> ler! Returns from makeMethod and the closure
> };
> }
>
> But I think it will be common mistake to put return in
> because you use return everywhere in Java.

Ok, there are two related issues here:

1) Programmers inserting a return statement because they're used to in inner classes.
2) Returning from a function that has already returned.

I don't think that number 1 is a problem because the syntax for anonymous functions is so different. It might happen 1-2 times and then you know you don't need a return unless you're doing something special.

Number 2 is more interesting. I think the right solution is to just throw a runtime exception, because this is the most expressive. It's possible to analyze statically if the return is wrong (i.e. return from a function that's no longer active), but this is not easy, and you will lose some expressiveness no matter how good your static analysis is. In your example, how are you going to tell that Collections.each doesn't store the anonymous function in a datastructure? There are (complicated) ways to determine this, but you will have to break down abstraction barriers: the caller has to know how the callee is implemented (for example, it has to know if the callee stores the closure in a data structure). And other problems appear: sometimes it *is* correct to store such a closure in a data structure, you just shouldn't call it after the enclosing function has returned. For example:

int foo(){
  Bar x = new Bar();
  x.setQ({z => return z;});
  x.callQ(3);
}


This is more common than you might think. For example, if you want to loop over two collections with the same loop body you want to be able to store a closure in a local variable and call:

collection1.each(theClosure);
collection2.each(theClosure);


For example if you want to determine if some element is present in collection1 or in collection2:

theClosure = {x => if(x==theElement){return true}};


It's quite possible that I am misunderstanding you entirely, I am sure that because you have written a proposal you have thought about this already.

p.s. An alternative solution is to *allow* non local returns, and revert control flow back into the enclosing function if you call the anonymous function. This is possible and it resembles continuations. Sadly it's unfeasible for Java.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 7, 2008 1:58 PM
Reply to this message Reply
> Ok, there are two related issues here:
>
> 1) Programmers inserting a return statement because
> they're used to in inner classes.
> 2) Returning from a function that has already returned.
>
> I don't think that number 1 is a problem because the
> syntax for anonymous functions is so different. It might
> happen 1-2 times and then you know you don't need a return
> unless you're doing something special.
>
> Number 2 is more interesting. I think the right solution
> is to just throw a runtime exception, because this is the
> most expressive.

For 1 I think it might be a common mistake if you are refactoring between closures and methods. For 2 I think the answer is to name what you are returning from so that the compiler can catch the error.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 8, 2008 1:13 AM
Reply to this message Reply
> For 2 I think the answer is to name what you are returning from so that the compiler can catch the error.

I don't see how that fixes the problem. Do you simply disallow placing closures with returns in local variables or data structures? I wrote about this option in my post, it is far too restrictive and very hard to check.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 8, 2008 1:09 PM
Reply to this message Reply
> > For 2 I think the answer is to name what you are
> returning from so that the compiler can catch the error.
>
> I don't see how that fixes the problem. Do you simply
> disallow placing closures with returns in local variables
> or data structures? I wrote about this option in my post,
> it is far too restrictive and very hard to check.

The method I have proposed for this is to implement the non-local return as a checked exception that has a synthesized name that is only available within the method the closure is defined in:

static <T> boolean isKeyPresent( List<T> list, T key ) {
  Collections.each( list, {T x => if ( x.equals( key ) ) return true; } );
  return false;
}


Becomes:

static <T> boolean isKeyPresent( List<T> list, T key ) {
  class IsKeyPresent$Return extends ReturnException {}
  final IsKeyPresent$Return isKeyPresent$Return = new IsKeyPresent$Return();
  try {
    Collections.each( list, {T x => if ( x.equals( key ) ) { 
      isKeyPresent$Return.setValue( Boolean.True );
      throw isKeyPresent$Return; 
    } );
    return false;
  } catch (IsKeyPresent$Return notUsed) {
    return (boolean) isKeyPresent$Return.getValue();
  }
}


ReturnException is a checked exception and each throws IsKeyPresent$Return (which is generically defined in each) so it is OK to pass the closure to each, but if you pass the closure to a method that doesn't throw the right exception then the compiler complains. You can't define a method accidentally that throws the right exception because IsKeyPresent$Return is an inner class to the method and hence only available inside the method. You could define a method that threw ReturnException or a generically defined exception but then you would have to deal with that checked exception, probably throw it like each does. If you define a method that threw ReturnException then you are saying in effect "trust me, I know what I am doing".

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 9, 2008 11:24 AM
Reply to this message Reply
How do you detect this error:

int foo(){
  if(this.isFirstCall){
    this.callMeLater = {x => return x;};
    this.isFirstCall = false;
  }else{
    this.callMeLater.invoke(2);
  }
  return 3;
}
 
...
 
foo();
foo();


(where this.isFirstCall and this.callMeLater are instance variables)

This should report an error on the second invocation of foo() because the return statement inside the anonymous function should return from the first invocation of foo().

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 9, 2008 12:55 PM
Reply to this message Reply
@Jules,

In my reading of the closure proposal from Neal your example would not be an error, the closure would return from the second foo. Neal am I correct?

In my own C3S (http://www.artima.com/weblogs/viewpost.jsp?thread=182412) I was careful not to use the term closure but instead say inner class. I understand that using closure in this context is confusing for people coming from other languages. I don't however think this behaviour of closures from other languages (capturing the stack frame) is a good design, I prefer exceptions.

So from the point of view that capturing the stack frame is not a good idea, assuming my reading of Neal's proposal is correct, I can see why Neal chose the term closure.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 9, 2008 8:29 PM
Reply to this message Reply
@Jules,

A language normally taken as having closures, Ruby, doesn't do what you want either:

class Foo
  def initialize
    @is_first_call = true
  end
  
  def bar
    if @is_first_call
      puts "First"
      @call_me_later = proc { | x | return x }
      @is_first_call = false
    else
      puts "Rest"
      puts @call_me_later.call( 2 )
    end
    return 1
  end
end
 
foo = Foo.new
puts "One"
puts foo.bar
puts "\nTwo"
puts foo.bar
puts "\nFinish"


Which when run prints:


One
First
1

Two
Rest
2
1

Finish


I am not a Ruby expert but I think the above is a valid translation of your code.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 10, 2008 2:17 AM
Reply to this message Reply
That's correct. Ruby always returns from the closure, not from the enclosing method. This is a good solution. It's less expressive, but it's more consistent with regular methods: return always returns from the method and if you don't use return, then the value of the last expression is returned.

I just realized that the return in closures should behave like this in Java too, because if statements are not expressions in Java:

lambda {
  if foo
     1
  else
     2
  end
}.call # returns 1 or 2


This is problematic with my behavior of return:

{x =>
  if(foo){
     1;
  }else{
     2;
  }
}.invoke


This doesn't work in Java, you need to return:

{ =>
  if(foo){
     return 1;
  }else{
     return 2;
  }
}.invoke


But this would, according to me, return from the enclosing method, so you'd have to do something like this:

{ =>
  int temp;
  if(foo){
     temp = 1;
  }else{
     temp = 2;
  }
  temp
}.invoke


But this is ugly.

I think Squeak Smalltalk has the return behavoir I described, but it is not a good fit for Java. Common Lisp has return-from wich is similar to your named return proposal.

Neal Gafter

Posts: 12
Nickname: gafter
Registered: Oct, 2003

Re: An Alternative to Closure Conversion and to Restricted Closures Posted: Jan 12, 2008 12:16 PM
Reply to this message Reply
@Howard: Re" In my reading of the closure proposal from Neal your example would not be an error, the closure would return from the second foo. Neal am I correct?"

No, you are not correct. The spec says: "Finally, an UnmatchedNonlocalTransfer is thrown when a return statement is executed if the method *invocation* to which the return statement would transfer control is not on the stack of the current thread."

Flat View: This topic has 30 replies on 3 pages [ « | 1  2  3 | » ]
Topic: An Alternative to Closure Conversion and to Restricted Closures Previous Topic   Next Topic Topic: Looking for Memories of Python Old-Timers

Sponsored Links



Google
  Web Artima.com   

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