r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[!REBOL3 Proposals] For discussion of feature proposals

Andreas
9-Nov-2010
[70x2]
If a function can't be rewritten so it doesn't use inner functions, 
you have no recourse.

What's that supposed to mean?
Making definitional return optional means that this option would 
need to be specified in the code that ''calls'' the functions that 
would benefit from it, rather than the functions themselves. This 
means that the option would need to be specified a lot.

What is that supposed to mean?


With optional definitional-return-only functions I would simply define 
a mezzanine USE with a definitional return, at which point it no 
longer swallows dynamic return. A caller of USE then does not have 
to worry about this at all.
BrianH
9-Nov-2010
[72x2]
you have no recourse

 is a polite way of saying "you are out of luck". At least regular 
 programmers would be out of luck there - I'm sure someone like Ladislav 
 could come up with an arcane workaround, or you could give up on 
 RETURN and EXIT and use another escape function instead, like THROW. 
 But I assume that you know what I meant by "recourse", and want the 
 point explained.


Pardon me, that question needs some background info. The return models 
are being used to deal with a basic problem of functions catching 
RETURN and EXIT when you don't want them to. This is the case with 
many mezzanine control functions which take and execute a block of 
code. We have been putting requests for new mezzanine control functions 
on hold for quite a while because they can't currently be made to 
pass through RETURN and EXIT, but USE and COLLECT got through before 
we started that, and the restriction is lifted now.


Let's use USE to illustrate, ignoring for the moment that USE *can* 
be rewritten so it doesn't use an inner function.

use: func [
    "Defines words local to a block."
    vars [block! word!] "Local word(s) to the block"
    body [block!] "Block to evaluate"
][
    apply make closure! reduce [to block! vars copy/deep body] []
]


USE uses an inner function to create a binding for its words (the 
closure!). For the dynamic return we have now, the inner function 
catches returns from the body, but even if it didn't the USE function 
itself would catch those returns as well.


One proposal to solve this would be to switch to definitional return, 
which means that RETURN and EXIT would be redefined in the code block 
of a function to return directly to that function, not any intermediate 
function in the call chain. This would solve returns being caught 
by the USE function itself, because the body of code that contains 
the 'return or 'exit words is not physically in the code of the USE 
function, it is only referenced by word. However, that block of code 
is used by the inner function as its code block, so the inner function 
would redefine those words and catch the returns.


If your function uses inner functions like USE does, and can't be 
rewritten to not use them, and you are using definitional return 
without the option to turn it off, then the inner function will localize 
RETURN and EXIT every time.


As a caveat, I wrote that phrase before I came up with the workaround 
in the next section of using direct references to the RETURN and 
EXIT function values instead of referring to them by name, which 
avoids the rebinding issues because no words are involved. See the 
code in http://curecode.org/rebol3/ticket.rsp?id=637to see what 
that workaround makes your code look like.
That was in answer to your first question. Now for the second.
Andreas
9-Nov-2010
[74x2]
That has nothing to do with inner functions.
You can safely use the inner function as long as you don't copy the 
code into the body of the inner function, which causes it to be rebound.
Gregg
9-Nov-2010
[76]
Thanks for all the effort that went into that doc! I've skimmed it, 
but will have to find time to read it in depth and digest it.
Andreas
9-Nov-2010
[77]
Of course, at this point you don't need to use an inner function 
at all, you just use bind + do.
BrianH
9-Nov-2010
[78]
USE uses the body of the affected code *as* the body of the inner 
function, so it *is* a good example of this pattern, if you ignore 
the fact that USE in particular can be rewritten so it doesn't use 
an inner function.
Andreas
9-Nov-2010
[79]
But the claim is even wrong for this example.
BrianH
9-Nov-2010
[80]
Keep in mind that we use USE to illustrate because it is simple and 
shows a lot of the problems we are trying to solve in its existing 
code. USE is being used as a standin for the many other functions 
that would be affected by these issues. The fact that USE can be 
rewritten doesn't mean that the other functions can also be rewritten.
Andreas
9-Nov-2010
[81]
All you have to do is construct a correct inner function, instead 
of an erroneous one.
BrianH
9-Nov-2010
[82]
USE's inner function is not erroneous, not in the slightest bit. 
The whole point of it is to rebind the code block passed to USE.
Andreas
9-Nov-2010
[83]
Yes, then simply rebind it properly.
BrianH
9-Nov-2010
[84]
It does rebind properly.
Andreas
9-Nov-2010
[85x2]
Your example does not. It improperly rebinds a definitional return.
Instead of only the local vars.
BrianH
9-Nov-2010
[87]
You grabbed that phrase that I was answering from the section where 
binding with definitional return was the only way to bind. There 
is no option in that section (and that fact is listed in the advantages). 
In that section, definitional is the only way to bind.
Andreas
9-Nov-2010
[88]
Sure there is. BIND + DO.
BrianH
9-Nov-2010
[89x3]
That is rewriting to not use an inner function. Which that phrase 
was referring to cases where that is not possible.
If you are claiming that there are no such phrases, fine. But assuming 
that such cases were possible was the premise that led to the "you 
have no recourse".
no such phrases -> no such cases
Andreas
9-Nov-2010
[92x2]
Which is hypothetical as it is easy to show there are no such cases. 
So inner functions have nothing to do with it.
And even if you insist on that hypothesis, the claim is still wrong.
BrianH
9-Nov-2010
[94x3]
Sorry, inner functions are the easiest way to implement certain things. 
And we need to consider that too-complex functions or ones that use 
arcane workarounds are getting rejected for that reason alone nowadays. 
Most of the native changes in the last 3 releases of R3 have been 
to deal with situations like this. For these proposals, easy trumps 
possible.
So even the FOREACH workaround would be enough to prompt a change 
now. For that matter, the most recent module system rewrite was specifically 
for that reason. The new module system doesn't do anything that the 
pre-108 rewrite didn't do, but it is easier to use. Easy by default 
is the top priority.
The only reason definitional-only might be chosen is because it is 
easier to understand than the other models. That alone might trump 
the places where it still doesn't work. I hope not, because the "option 
to not rebind RETURN and EXIT" is really simple to specify and much 
easier to do than the workarounds that you have to do if you don't 
have that option, workarounds like your BIND + DO, especially when 
you consider 'self issues that prevent you from using objects for 
this kind of thing (hence the FOREACH).
Andreas
9-Nov-2010
[97x2]
So the real disadvantage in the "inner function" paragraph is that 
you can not write a function which does not rebind RETURN.
(Or at least that's the claim.)
BrianH
9-Nov-2010
[99x2]
Yes. I'm sorry for the confusing phrasing.
So assume that in this case "not possible" could in some cases mean 
"not possible to get accepted as a mezzanine because it's too Scheme-like" 
or "not possible for a regular programmer to come up with a workaround 
like this", because both limitations have real examples that have 
already manifest.
Andreas
9-Nov-2010
[101]
More precisely, the disadvantage is that we don't have an any-function! 
constructor which does not rebind RETURN in the function body.
BrianH
9-Nov-2010
[102x3]
And the claim is false because of the workaround with the direct 
function value references, which I came up with later. Only the words 
are rebound.
We actually need that option for both function! and closure! types, 
because of their different contexts. It is easier to add this as 
an option than as different types.
Especially since Carl is the one who suggested options in the first 
place, so we know it's possible to implement.
Andreas
9-Nov-2010
[105]
That's why I said any-function!.
Ladislav
9-Nov-2010
[106]

not possible to get accepted as a mezzanine because...not possible 
for a regular programmer to come up with a workaround like this" 
- why, then, was my CLOSURE constructor accepted?
BrianH
9-Nov-2010
[107]
No, it would need two added function types, not just one. And names 
for those types, and names for the wrapper functions that create 
those types, and so on. An option is easier.
Andreas
9-Nov-2010
[108]
We are talking past each other.
BrianH
9-Nov-2010
[109]
That was a backport, Ladislav. R2 has more lax standards. We are 
trying to cut back on that in R3, but R2 is a lost cause.
Andreas
9-Nov-2010
[110]
The disadvantage is that we don't have a function constructor which 
does not rebind return in the function body.
BrianH
9-Nov-2010
[111x2]
If you want to talk about intimidating R2 functions, look at APPLY 
or MAP-EACH. Neither of those would be accepted for R3.
Agreed, Andreas. I was just saying that a spec option is a better 
solution to that problem than new datatypes.
Andreas
9-Nov-2010
[113]
Yeah, and as I did not suggest new datatypes, I was wondering where 
that came from.
BrianH
9-Nov-2010
[114x2]
we don't have an any-function! constructor...
Guess I misinterpreted that :(
Andreas
9-Nov-2010
[116x2]
Which was meant to imply that we have neither a constructor for closure! 
nor for function! nor for any other user-definable function type 
:)
Misleading, though. Sorry for that.
BrianH
9-Nov-2010
[118x2]
One of the goals of the native changes in R3 is to cut down on the 
need for arcane workarounds in regular REBOL code. For every arcane 
workaround that you might see in a mezzanine, you will see the same 
workaround need to be done over and over again in user code. Usually 
badly. There have been a host of native changes to make regular REBOL 
code easier to write and read and cleaner to look at. We even got 
more of these in the last 3 releases.
For that matter, that is what the call for idioms is for.