Opportunistic Invisibility

When "Invisibles" were introduced, 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."

Due to changes in the way the evaluator interacts with invisibles, this doesn't need to be a rule anymore. There could be functions which decide--perhaps based on their arguments--whether to act invisibly or not.

This has actually been requested. Imagine you are dealing with something like DUMP, and you want to call out that you'd like to actually return one of the things you are dumping:

all [
    x * 10 + 20  ; want to print this expression and see its evaluated result


all [
    -- (x * 10 + 20)  ; now it's not participating in the ALL anymore

@gchiu wanted some way of getting a printout like -- (x * 10 + 20): 40 but also having the value still returned, without needing to name another operator. So maybe you could mark this, e.g. with a GET-GROUP! or GET-WORD! in the DUMP dialect.

Having this particular debug construct behave in that way may not be the best idea in the world.
But we don't need to technically prohibit it.

This could give an interesting nuance to the difference between return and return void. RETURN used alone could just mean literally "return and don't have any value, not even void". It's certainly a weird idea, but it's consistent...and we could make sure you knew what you were doing by requiring some kind of invisibility in the type signature of a function before allowing a 0-arg RETURN. Of course there could be other syntaxes, like return/invisible...which would probably fit practice better by leaving plain RETURN as returning void.

There'd need to be a new syntax for saying you could return invisibles, because return: [] as the indicator wouldn't allow you to specify it along with other types.

Worth Doing?

The main argument for opportunistic invisibles is to keep you from having to create different names for the invisible and visible forms of a function, when there could be a learnable convention for knowing which it is from the arguments alone.

The more fanciful argument is creating strange execution patterns in code golf, where you have some kind of "voting" mechanism e.g. inside an ALL and program behavior emerges from things opting out. This harkens back some to the "null opts out of logical operations" ideas from the early days of null behavior.

Normalizing invisibles to the point of saying that they're what you get when you just do a plain RETURN is a fairly weird idea, but they're a pretty powerful feature that might have even more applications than yet thought of. most free-form programming language ever created is still the goal, so... I think this deserves some consideration.


On the plus side of saying that RETURN with no arguments means "invisible" is that it facilitates wrapping.

Imagine if MAYBE-INVISIBLE is a function that might return a value, or might be invisible. Then you want to write a wrapper for it:

wrapper: func [x] [
    if x = 10 [return 20]
    return maybe-invisible x

In the event that MAYBE-INVISIBLE vaporizes, this will act like you had written just a plain return. So if plain RETURN means invisible, then you get an effective pass-through of the invisibility.

...BUT...On the minus side is that it doesn't play very well with the idea that a variadic RETURN could be used with multiple return values somehow. Imagine if return ret1 ret2 could line up with a callsite that said [x y]: ... where x would get ret1, and y would get ret2.

That Aside, Opportunistic Invisibility is Almost Implemented

It's tricky to write. But I think of it ultimately as a "simplifying" aspect on the evaluator...as it removes the separate concept of an "invisible function".

There are some things that can't be done if you don't know a-priori that a function is invisible...and have to wait to find out until after it has run that it is. But I think few people would notice the subtlety of what that implies.

This puts some pressure on type specs, because now we have another return state that isn't an ANY-VALUE! besides null. The return: [] is no longer going to work.

What I have right now is that if you want to say that a function is invisible even if it just drops out of the bottom with no RETURN statement, then you say return: <void> This is like the return: <none> instruction in that it overrides whatever else would fall out.

Then there's the question of how to put a non-datatype in the spec list for the return. This is something that can only apply to RETURN...which makes it a whole-function property more than a

I've been wondering if ISSUE! is better for these states (#null, #vanish) and using 7 characters or less on 32-bit platforms has a nice savings over <opt> and <invisible>.

As usual, no shortage of things to think about. But invisibility has become a core differentiating feature...it's novel and it's powerful. Embracing it systemically is a good thing.

1 Like

Behold, the first opportunistic invisible:

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

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

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

Which was followed immediately by the first chaining opportunistic invisible:

>> vanish-if-even: func [return: [<void> integer!] y] [
       return vanish-if-odd y + 1

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

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

Like I say...implementation-wise, it's a bit of a mixed bag in terms of making some things simpler and some things more complex. But as a feature, I think it's something that will open up space for exploration...especially in debug constructs. Should be very fun for code golf. :golf: