Bool f()
{
[1, 2, 3].each | Int n |
{
if (n == 2)
{
return true // line 15
}
}
return false
}
is giving me:
/Users/cbeust/fan/a.fan(15,16): Expected end of statement after return in Void method
What am I missing?
brianWed 25 Jun 2008
Currently return is always scoped to the inner most closure. So your return is applied to the closure, not the outer most method.
There has been a bit of talk about non-local returns, sometimes I wish I had that. But it would have to be implemented with an exception - and when the closure gets passed outside the method scope things get dicey.
I should note though that most of the time that can be solved functionally:
There are definitely different opinions on this subject. What's expected to one person is not expected to another.
I personally just want an easy way to avoid having to say return for really short inline closures (such as above). It would be great to be able to say return [1, 2, 3].any {it == 2}. But I'll also survive with the current rules.
brianWed 25 Jun 2008
It would be great to be able to say return [1, 2, 3].any {it == 2}.
The "return the last expression" is a nice shortcut. But I think it is kind of weird in a non-functional language which is statement based versus expression based. That is why I've left the return keyword. It is a bit of extra verbiage, but I think it is nice to see where your exit points are.
I have a feeling, the non-local return will crop up again. I think Stephen brought it up using something like break.return
cbeustFri 27 Jun 2008
I think it will be necessary to support both local and non-local returns in closures.
FYI, the current Java proposal (called BGGA) does the opposite:
1) return returns from the method 2) for a local return, you just use an expression (downside: you can only have one)
Point 1) is obviously there for backward compatibility, which is not a concern for Fan. I think 2) doesn't work well, neither in Java nor in Fan, so maybe a different keyword will be in order (localreturn? closurereturn? Ugh, I realize that I'm not a big fan of keywords made of two words)
-- Cedric
heliumFri 27 Jun 2008
Void f()
{
foo := someClosureContainingAReturn
bar := foo() // What happens here?
}
tacticsFri 27 Jun 2008
I think the current setup is nice and predictable. It is a bit wordy having to put a return in, but the semantics are very clear for anyone who has done anything with closures before.
brianFri 27 Jun 2008
I think the current design is the easiest to understand and the most internally consistent, return exits from a method/function. Although at the same time, I do sometimes which I had a non-local return or break inside an each closure. But it hasn't been anything I couldn't pretty easily work around.
jodastephenFri 27 Jun 2008
As well as BGGA, there are two other main closure proposals in Java - CICE and FCM. I am co-author or FCM, and we deliberately chose to not support non-local returns as they are very error-prone. (There are examples on my blog of some of the issues, and I've discussed them at talks too - http://skillsmatter.com/podcast/java-jee/java-closures-comparing-the-choices)
In particular you get unexpected exceptions when you use a non-local return after the original method is complete, and no exceptions and no compile errors when you used a non-local return but meant a local one.
Looking across languages, the general rule seems to be that languages which only have expressions tend to support non-local returns (eg. Scala). Languages that are C family with statements and expressions use return for local returns (eg. Groovy, Nice, C#, C++ proposal, FCM/CICE closures for Java).
In fact, BGGA is very much an odd proposal in this respect, and exists solely because of the control invocation syntax (define your own keyword) in the proposal. Personally, I don't think that BGGA non-local returns will make it to Java.
With FCM, I have said the a dual keyword could be used to force a non-local return. This could be break return, throw return or methodName.return. But overall, I don't think the use cases justify the extra complication.
tompalmerFri 27 Jun 2008
This discussion has convinced me that closures in fan should require the leading |,|. If returns are always local to the function, it should be an obvious function, so no method {...} for closures is my current vote. Or in other words, I'm happy with current Fan syntax and semantics for "with" blocks, closures, and returns (at least so far as I understand the details).
cbeustSun 29 Jun 2008
Oh hi Stephen, I didn't recognize you :-)
I was discussing this very topic with Josh at Jazoon last week. As you know, he is not a big fan of BGGA either (and he's a co-backer of CICE) and he pointed out a few quirks with non-local returns as well.
Still, I don't think we should give up on having them, especially in a brand new language which is not burdened by backward compatibility. I was tripped by this pretty quickly (which is why I started this thread), so I suspect that I won't be the only one...
-- Cedric
tompalmerWed 2 Jul 2008
I think another confusion comes from the block syntax outside parens. For example, if we had to write it ECMAScript-like instead of Ruby-like, we'd get this:
Bool f()
{
[1, 2, 3].each(| Int n |
{
if (n == 2)
{
return true // line 15
}
})
return false
}
Inside the parens, it seems more like the return ought to be local. Something to consider.
On a different note, in my toy language, I have two keywords, def and do. As in Ruby, Python, Scala, and probably others, def is used for defining methods/functions. And as in ECMAScript, I used def for anonymous functions (only in ES, the keyword is function). These use local returns. So return always exits from the nearest enclosing def. On the other hand, do is an anonymous function that is expected to execute synchronously. So, for example:
# My vapor language June here, not Fan. And just here for a diversion.
def f(): Boolean {
[1, 2, 3].each do(n) {
if (n == 2) {
return true # Return here exits "def f()"
}
setInterval(10) def() {
# Do stuff later. Return exits the anonymous def
return whatever
}
}
return false
}
This is sort of like Neal Gafter's => vs. ==>, but I think the meaning is more obvious.
My understanding is that in Fan, the compiler or runtime figures out automatically whether the closure is safe to execute in other threads (and note that threads vs. when-executed aren't exactly the same issue on this subject). Is this correct?
Anyway, not a big deal, but I think the paren enclosure issue ought to be considered for Fan.
tacticsWed 2 Jul 2008
Every language has it's gotchas. At least type checking can help alert people to this. Though this thread is evidence that eventually when we have a nice tutorial for beginners, that's something we should point out.
cbeust Wed 25 Jun 2008
is giving me:
What am I missing?
brian Wed 25 Jun 2008
Currently return is always scoped to the inner most closure. So your return is applied to the closure, not the outer most method.
There has been a bit of talk about non-local returns, sometimes I wish I had that. But it would have to be implemented with an exception - and when the closure gets passed outside the method scope things get dicey.
I should note though that most of the time that can be solved functionally:
tompalmer Wed 25 Jun 2008
There are definitely different opinions on this subject. What's expected to one person is not expected to another.
I personally just want an easy way to avoid having to say
return
for really short inline closures (such as above). It would be great to be able to sayreturn [1, 2, 3].any {it == 2}
. But I'll also survive with the current rules.brian Wed 25 Jun 2008
The "return the last expression" is a nice shortcut. But I think it is kind of weird in a non-functional language which is statement based versus expression based. That is why I've left the return keyword. It is a bit of extra verbiage, but I think it is nice to see where your exit points are.
I have a feeling, the non-local return will crop up again. I think Stephen brought it up using something like
break.return
cbeust Fri 27 Jun 2008
I think it will be necessary to support both local and non-local returns in closures.
FYI, the current Java proposal (called BGGA) does the opposite:
1)
return
returns from the method 2) for a local return, you just use an expression (downside: you can only have one)Point 1) is obviously there for backward compatibility, which is not a concern for Fan. I think 2) doesn't work well, neither in Java nor in Fan, so maybe a different keyword will be in order (localreturn? closurereturn? Ugh, I realize that I'm not a big fan of keywords made of two words)
-- Cedric
helium Fri 27 Jun 2008
tactics Fri 27 Jun 2008
I think the current setup is nice and predictable. It is a bit wordy having to put a return in, but the semantics are very clear for anyone who has done anything with closures before.
brian Fri 27 Jun 2008
I think the current design is the easiest to understand and the most internally consistent, return exits from a method/function. Although at the same time, I do sometimes which I had a non-local return or break inside an each closure. But it hasn't been anything I couldn't pretty easily work around.
jodastephen Fri 27 Jun 2008
As well as BGGA, there are two other main closure proposals in Java - CICE and FCM. I am co-author or FCM, and we deliberately chose to not support non-local returns as they are very error-prone. (There are examples on my blog of some of the issues, and I've discussed them at talks too - http://skillsmatter.com/podcast/java-jee/java-closures-comparing-the-choices)
In particular you get unexpected exceptions when you use a non-local return after the original method is complete, and no exceptions and no compile errors when you used a non-local return but meant a local one.
Looking across languages, the general rule seems to be that languages which only have expressions tend to support non-local returns (eg. Scala). Languages that are C family with statements and expressions use
return
for local returns (eg. Groovy, Nice, C#, C++ proposal, FCM/CICE closures for Java).In fact, BGGA is very much an odd proposal in this respect, and exists solely because of the control invocation syntax (define your own keyword) in the proposal. Personally, I don't think that BGGA non-local returns will make it to Java.
With FCM, I have said the a dual keyword could be used to force a non-local return. This could be
break return
,throw return
ormethodName.return
. But overall, I don't think the use cases justify the extra complication.tompalmer Fri 27 Jun 2008
This discussion has convinced me that closures in fan should require the leading
|,|
. If returns are always local to the function, it should be an obvious function, so nomethod {...}
for closures is my current vote. Or in other words, I'm happy with current Fan syntax and semantics for "with" blocks, closures, and returns (at least so far as I understand the details).cbeust Sun 29 Jun 2008
Oh hi Stephen, I didn't recognize you :-)
I was discussing this very topic with Josh at Jazoon last week. As you know, he is not a big fan of BGGA either (and he's a co-backer of CICE) and he pointed out a few quirks with non-local returns as well.
Still, I don't think we should give up on having them, especially in a brand new language which is not burdened by backward compatibility. I was tripped by this pretty quickly (which is why I started this thread), so I suspect that I won't be the only one...
-- Cedric
tompalmer Wed 2 Jul 2008
I think another confusion comes from the block syntax outside parens. For example, if we had to write it ECMAScript-like instead of Ruby-like, we'd get this:
Inside the parens, it seems more like the return ought to be local. Something to consider.
On a different note, in my toy language, I have two keywords,
def
anddo
. As in Ruby, Python, Scala, and probably others,def
is used for defining methods/functions. And as in ECMAScript, I useddef
for anonymous functions (only in ES, the keyword isfunction
). These use local returns. Soreturn
always exits from the nearest enclosingdef
. On the other hand,do
is an anonymous function that is expected to execute synchronously. So, for example:This is sort of like Neal Gafter's
=>
vs.==>
, but I think the meaning is more obvious.My understanding is that in Fan, the compiler or runtime figures out automatically whether the closure is safe to execute in other threads (and note that threads vs. when-executed aren't exactly the same issue on this subject). Is this correct?
Anyway, not a big deal, but I think the paren enclosure issue ought to be considered for Fan.
tactics Wed 2 Jul 2008
Every language has it's gotchas. At least type checking can help alert people to this. Though this thread is evidence that eventually when we have a nice tutorial for beginners, that's something we should point out.