Why can't ELIDE come after a FAILURE! ?

You can use ELIDE in a function if you want its previous result to be what drops out, e.g.:

 foo: lambda [x] [
     bar x
     elide print "print this but still return result of (bar x)"
 ]

But this doesn't work if bar x returns a FAILURE!, because even though ELIDE is vanishable and returns VOID!, the failure gets turned into a PANIC before it gets a chance to run.

Couldn't vanishable functions delay the escalation to panic, and just pass through the FAILURE! if they produce VOID!?

So this is a tough one.

FAILURE! is intended to be a means of doing control flow... giving you a chance to triage a "hot error" result, and if you don't triage it then escalating to an "exception".

Something like CALL uses FAILURE! to tell you the shell command didn't succeed. We don't want to run the second CALL here if the first has a FAILURE!:

call [...command1...]  ; you *could* use `try call` or `except` etc. here
call [...command-thats-bad-to-run-if-command1-failed...]

But the problem is we don't know if CALL is going to return VOID! until you run it.

I don't know if the semantic of vanishing really has anything to do with the idea that you don't expect the previous statement to have succeeded.

e.g. ELIDE isn't necessarily a signal of "erase this value because what I'm doing is out-of-band-debugging". Whatever you're doing could hinge on the success of the previous statement just as much as any other in-sequence function call.

I think the unfortunate consequence is that if you're dealing with constructs that produce FAILURE!, you can't expect invisible functions after them to erase their result and give the failure. At least not by default.

Available Workarounds?

The concept of the historical "ALSO" (evaluate this and return it, but also do this other thing) doesn't have the problem, let's call that ALSO2:

foo: lambda [x] [
     also2 // [
         bar x
         print "print this but still return result of (bar x)"
     ]
 ]

But I hate ALSO2, and I'd rather have some kind of construct that just runs code in-turn but erases anything with a certain pattern of list delimiters:

foo: lambda [x] [
     eraseval:{{}} [
       bar x
       {{print "print this but still return result of (bar x)"}}
     ]
 ]

So it would be COMPOSE-like, but instead of composing ahead of time it "composes" as it goes.

We could say that the ^ identity operator folds in and triggers this deferral:

foo: lambda [x] [
    bar x
    ^ elide print "print this but still return result of (bar x)"
]

If you use that operator you're getting dicey behavior already, so what if it gets a little bit dicier?

That feels slightly disconcerting but when I think about the situations in which it's applied, it might actually be appropriate.

TL:DR; Probably Useful Behavior But Shouldn't Be Default

Vanishing doesn't by default imply that you want to ignore the natural rules of statement sequencing.

Functions like debug DUMP which you want to inject at arbitrary positions...even after a failure...might want to break the rule. But then again, they might not. It's hard to read people's mind.

Given that you can't read people's minds, an operator for requesting the behavior--which the evaluator stepper can recognize--might be useful. ^ might be an appropriate operator, which the evaluator that coordinates multiple steps already has to heed.