Opportunistic Invisibility

When "Invisibles" were introduced, the evaluator had to establish if a function was either always-invisible or always-not-invisible. You couldn't make a function that "sometimes returned an INTEGER!, and sometimes was invisible."

This restriction was eventually lifted. But then raised the question of how a RETURN statement would return invisibly.

The idea came along that RETURN would detect whether to return invisibly or not based on whether it was arity-0 or arity-1. Here is the first version of what opportunistic invisibility looked like:

>> vanish-if-odd: func [return: [<invisible> integer!] x] [
       if even? x [return x]
       return  ; arity-0 return meant invisible
   ]

>> <test> vanish-if-odd 2
== 2

>> <test> vanish-if-odd 1
== <test>

What was clever about the arity mechanic was that it could be chained. If you RETURN'd the result of an opportunistic invisible, then if it happened to vanish you'd effectively make an arity-0 RETURN.

>> vanish-if-even: func [return: [<invisible> integer!] y] [
       return vanish-if-odd y + 1  ; if vanishes, RETURN perceives arity-0
   ]

>> <test> vanish-if-even 2
== <test>

>> <test> vanish-if-even 1
== 2

Alas, There Were Problems

One problem is that the variadic return was subject to line continuation "arity bugs".

For example, imagine trying to do a return in the middle of a block of code, with the intention to not running the ensuing code:

 foo: func [return: [<invisible> ...]] [
     ... some code here ...
     return  ; temporarily insert a return for debugging 
     ... some more code ...
 ]

A casual reader would think that would return invisibly, when it would actually return (... some more code ...), picking up what's on the next lines.

Even trickier was that invisibility itself--and especially opportunistic invisibility--had the power to wreck code structure to where it was completely unpredictable. The remedy was that invisibility could only apply in "interstitial slots", meaning that RETURN could not take an invisible as an argument.

New Mechanics Build On NIHIL (Isotopic Empty BLOCK!)

Years of puzzling over the quirks of how to coherently wrap and process invisible functions led to rebuilding the feature on top of meta-representations and isotopes. You can read all about that evolution in "Invisibility Viewed Through Modern Eyes". :eyes:

RETURN uses a special ^META parameter convention to allow it to accept the signal for invisibility as an argument. (Under this convention it receives the quasiform ~[]~, which is the meta of an empty unstable isotopic parameter pack used by multi-return.)

So an opportunistically invisible function would now look like this:

>> vanish-if-odd: func [return: [<nihil> integer!] x] [
       if even? x [return x]
       return nihil
   ]

>> <test> vanish-if-odd 2
== 2

>> <test> vanish-if-odd 1
== <test>

While such constructs can still give rise to tricky situations in code, it's powerful while being much more under control.

2 Likes