metaeducation / rebol-issues

6 stars 1 forks source link

CATCH /name true option #1520

Open rebolbot opened 14 years ago

rebolbot commented 14 years ago

Submitted by: BrianH

CATCH/name true would catch all THROW and THROW/name of any words thrown. This option would mostly be used for test code and calling third-party code from robust frameworks.

This would probably be a rarely used option, as normally you only want to catch the stuff you specify that you want to catch. However, in those cases where you need this you will really need this.

CATCH/name could be combined additively with the /quit and /halt options - there would be no conflicts. The logic! type would be added to the typespec of the word argument. CATCH/name false would be a noop, not catching anything, though /quit and /halt would still add to that. Sorry about the noop, but it made no sense to do CATCH/name none and have that mean all throws, and logic! has two values - perhaps it could be used for debugging.

See #1518 for the other part of this proposal.

>> catch [throw/name 1 'foo 'not-reached]
** Throw error: no catch for throw: make error! 2
>> catch/name [throw 1 'not-reached] true
== 1
>> catch/name [throw/name 1 'foo 'not-reached] true
== 1
>> catch/name [throw 1 'reached] false
== reached
>> catch/name [throw/name 1 'foo 'reached] false
== reached

CC - Data [ Version: alpha 97 Type: Wish Platform: All Category: Native Reproduce: Always Fixed-in:none ]

rebolbot commented 14 years ago

Submitted by: abolka

If current CATCH behaviour is changed as described in #1518, we'll definitely need a possibility to CATCH all THROWS, whether named or not. So +1 for a CATCH/all catching all THROW unwinds.

I strongly disagree that CATCH/all should catch other (non-THROW) unwinds as well. This would be highly counterintuitive as the obvious and primary use of CATCH is to catch THROWs. I think code such as

  run-plugin: func [f [any-function!]] [
    catch/all [
      return apply :f []
    ]
    "ERROR: f tried to THROW"
  ]
is quite conceivable for an intermediate REBOL user to write. I don't want to explain to this hypothetical user that CATCH/all not only catches all THROWs but also "disables" RETURN without obvious reason.

Whether a facility for catching BREAK/CONTINUE/RETURN/EXIT unwinds is needed at all, will be a direct result of how bug #1506 is resolved. In any case, if such a facility is implemented, it should be under a separate name, such as CATCH/unwind.

This would result in:

  catch: Catches an unnamed throw
  /name: Catches a named throw
  /all: Catches all throws, named and unnamed
  /unwind: Special catch for the BREAK, CONTINUE, RETURN, EXIT natives
  /quit: Special catch for the QUIT native

To catch absolutely all unwinds catchable (i.e. excluding QUIT/now and HALT, as suggested by Brian) one would therefore use CATCH/all/unwind/quit.

rebolbot commented 14 years ago

Submitted by: BrianH

"as described in #1518"

That term "unwind" refers to the implementation of those functions; it's a little too geeky to use as a option name here, too hard to explain. Perhaps /other would be better for abolka's revision if that gets adopted.

On the other hand, if the RETURN and EXIT functions go definitional and stop being normal unwinds, and we don't catch HALT and QUIT/now, that would leave just QUIT, BREAK and CONTINUE. If that is the case then it's not worth the hassle to catch anything other than THROW and THROW/name with CATCH/all. CATCH/quit and LOOP have no BIND/copy overhead so we can use them directly in a CATCH-ALL mezzanine. The only ones with overhead to catch otherwise are RETURN and EXIT, which usually need to be caught by creating a function and executing it, and wouldn't work at all to catch unhandled RETURN and EXIT if they are definitional.

The revised proposal would then be to have CATCH catch THROW (but not THROW/name); CATCH/quit catch QUIT (but not THROW, same as current behavior); CATCH/name catch THROW/name of the same name; CATCH/all catch THROW and THROW/name for all names (but not QUIT); and CATCH/all/quit catch QUIT, THROW, and THROW/name for all names. And CATCH/name/quit would probably ignore the /name option and not catch THROW/name, the same as it behaves now.

rebolbot commented 14 years ago

Submitted by: BrianH

OK, time to scale back this proposal. We already have a way to catch RETURN and EXIT (DOES), BREAK and CONTINUE (LOOP 1), THROW without /name (CATCH), and QUIT (CATCH/quit). #1742 proposes a way to catch HALT (likely CATCH/halt), and #1743 would deal with QUIT/now (by getting rid of it). So we don't need to catch those with this proposal, they're covered, and we want to be able to catch those things separately.

We need to rethink CATCH. I think the best model is to have the default be minimal, with CATCH only catching THROW. CATCH/quit would only catch QUIT. CATCH/halt would only catch HALT. CATCH/name word would only catch THROW/name of the EQUIV? word. CATCH/name true would catch all THROW and THROW/name, with whatever words. And you can combine the options in an additive way, with CATCH/quit/halt/name true catching QUIT, HALT, THROW and THROW/name for all names. No other options needed, no potentially conflicting options.

I'm changing this proposal to just the CATCH/name true variant. #1742 can be CATCH/halt, and we have CATCH/quit already (though see #851 and #1743). And #1744 would make CATCH/name word more useful.

rebolbot commented 14 years ago

Submitted by: Ladislav

I still oppose the #1518. A locally bound THROW looks preferable (since it "automatically" looks for its intended target, without needing additional arguments).

The fact, that we can catch all THROWs using CATCH is good, the only thing that may be questionable is the current way how the CATCH function is used. But, the habits should not force us to complicate our code, the interpreter, and the language.

rebolbot commented 14 years ago

Submitted by: BrianH

Catching all throws is a rarely needed feature, only needed by makers of test frameworks and callers of untrusted code (like you, Ladislav).

Most people need to be able to skip past catches, which is the best reason for having THROW/name, and because of #1518 they can't. This means that for most uses, there is no difference between THROW/name and THROW. But there needs to be a difference, there needs to be a purpose for having THROW/name that justifies its existence, otherwise we should drop the feature altogether. And there is no reason we should drop such a potentially useful feature as THROW/name.

The purpose of #1518 is to make the most commonly needed behavior the default, and the purpose of this ticket is to make the more rarely needed behavior of catching all throws an option instead. Both types of behavior are important in different circumstances, but being able to get past CATCH by default is more important for most programmers than the catch-all behavior, even though the catch-all behavior is more important for you.

You have stated repeatedly that it is provable that you can do the same stuff without such changes, and I have seen the code that you use to prove this. The problem is that is really advanced code, and most people other than you and maybe a half-dozen others (including me) understand what you are writing, let alone would be able to write such code themselves. And why should they, when it would be so much easier to just make a simple feature for them to use without needing to understand how it is done? And so such a feature was added: THROW/name. But it doesn't work well enough to be useful because of the CATCH bug, which you consider to be a "feature" for your code, but for most people basically disables most of the THROW/name feature, so much so that it mostly isn't used anymore.

There's no reason we can't support both uses, yours and the rest of the world's. We just need to change the default.

And once we change that default, we won't need to do it again. Once THROW/name works, we can use it to implement in mezzanine all of the obscure requests for new control functions. And once this ticket and #1742 and #1743 are implemented, we will have a clearly defined way to catch everything if we really need to. No more arms race, everyone wins.

rebolbot commented 14 years ago

Submitted by: maxim

since I also think that #1518 is essential to make more advanced code patterns easy to implement by non god-like rebolers, this wish is also pretty important IMHO.
in addition to the above proposal, I think that 

CATCH/name [...] false could be used to catch ONLY /name throws but any word matches.

the idea being that the logic argument means: "also catch non-named throws?"
with this setup, we effectively can catch any combination of unnamed, specific or all named and both (named and unnamed) throws. 
rebolbot commented 14 years ago

Submitted by: BrianH

That sounds good to me, Maxim.