The Fuse Operator - A Suggested Language Extension

Perl 5 has become pretty stable, but there is always room for small improvements. I would like to discuss yet another "missing" operator. Its purpose is to make expressions handle some edge cases more gracefully. It could render some other extensions that have been suggested before unnecessary.

Previous Art

There has already been some discussion about safe dereferencing. This means dereferencing in a way that neither auto-vivificates undefined values nor chokes over attempts to call methods of undefined objects.

Examples:

$name = $person  -> {'name'};     # may create a hash
$name = $person ?-> {'name'};     # leaves $person alone
$name = $person  ->   name;       # chokes on undef
$name = $person ?->   name;       # passes undef on

Note that the question-marked arrow here stands in for whatever symbol would have been chosen for the safe dereferencing operator. There have been many candidates with no undebated favourite.

Note also that stopping auto-vivification and allowing method calls on undef are two different notions not everybody would like to see combined.

A More Generic Approach

I can see a more general concept behind the cases the safe dereferencing operator was meant to address. What if we had a way to safely bail out of all sorts of expressions upon a check for definedness? The situation where we want to perform safe dereferencing would be one possible occasion for this. Others could be arithmetic expressions or string manipulations. And bailing out here does not only mean carrying on with a value of undef, but actually cutting useless branches short, like boolean operators do.

Details

I am suggesting the symbol ^^ for our new operator. It is a unary postfix operator like the postfix variant of the auto-increment operator ++, which also shares its precedence level and some aspects of its visual appearance.

Its high precedence makes sure it attaches to a single term in a numerical, string or binary expression. Being well-behaved if somewhat boring, it does not change its one operand. It just checks whether it is defined.

If so, the expression is evaluated as if the fuse operator wasn't there. If not, the fuse blows, and evaluation short-cuts to the next part on the precedence level of || and //, or to the end if no parts of such low precedence follow. A blown fuse makes its subexpression evaluate to undef.

Examples:

# If velocity is defined, use it, otherwise don't.
# Note that just the concatenation arg is guarded,
# so you still get an error if $b is not a hashref.

$a = $b->{'velocity'}^^ . ' mph' // 'unknown';

# Safe method calling on $d (it may be undefined).
# Note that the second dereference is normal,
# so parent() returning undef would trigger an error.

$c = $d^^ ->  parent  ->  name;

# Safe dereferencing of $d (it may be undefined).
# Note that the second dereference is normal,
# so a nonexistent 'parent' component would be created.

$c = $d^^ ->{'parent'}->{'name'};

Notes

It is important to grasp the precedence level of short-cuts introduced by the fuse operator. In particular, short-cuts won't escape out of parentheses or across commas. So, e.g., you can't jump out of a function call from within the argument list, say.

# these two are equivalent:
$a = foobar($b^^);
$a = foobar($b);

# this would be safer:
$a = defined($b)? foobar($b): undef;

If you do want to get out of some nested structure, you might have to wire more than one short-cut:

$f = ($g | $h^^ | $i)^^ & $j;

*

Another definedness-aware operator that has been suggested in the past is defined-and. I am voting against that one, as defined-and can quite easily be emulated using existing operators.

# these two are equivalent:
$a =         foo() ?&& bar();
$a = defined(foo())?   bar(): undef;
Note that the question-marked double ampersand here stands in for whatever symbol would have been chosen for defined-and.

*

Inside quoted strings, carets or double carets should not have new special meanings. To guard against undef being expanded into a string, take it outside the quotes and stick a fuse op next to it:

$a = "hello $b";       # may trigger a warning
$a = "hello $b^^";     # may still trigger a warning
$a = 'hello ' . $b^^;  # may evaluate to undef

*

While auto-increment and auto-decrement can only be applied to modifyable values (L-values), a fuse can adorn anything. If it is applied to an L-value, the result will keep being modifyable:

$a = $b^^++;       # increment $b only if it is defined
$a = $b++^^ * $c;  # increment $b, use old value if defined

*

Maybe someone was hoping the double caret would some day be used for a medium-level precedence version of xor. While I don't see much demand for that, it would not be impossible to have, even with the fuse operator in place. Perl parsers would just have to distinguish postfix from infix usage. I'd rather leave infix ^^ out of the language, however, to make the usage I do like to get established better stand out.

*

My first idea of a name for the operator was "lightning-rod". Other alternatives were "definedness guard" and "bat". A bat hangs somewhere beneath the ceiling and can grab something that is not too heavy for it to lift, and put that down elsewhere. I don't want to speculate on possible uses for a wumpus operator right now, though.

Conclusion

The defined-or operator // introduced in Perl v5.10 gave us a very useful option for short-cutting on definedness. It makes Perl expressions a tad more expressive. Expanding on this notion, I am now presenting a candidate for a fine companion. It would be more than a replacement for the safe dereferencing operator that seems to be so hard to agree upon.

It hasn't been implemented yet. This blog post (following a first draft on perlmonks.org) is intended to get Perl folks interested. Finally, of course, it is upon perl5porters do decide.

3 Comments

Does Perl 6 have such an operator? If so, it might be wise to look into its syntax and semantics.

Perl 6 has a heaping pile of operators, but I do not know of one that works like this. There are some operators and conditions that may mitigate the need for this operator, though.

Here are some examples of how I would think to write each of the examples given above for the fuse operator in Perl 6:

$a = do with $b { .<velocity> ~ 'mph' } else { 'unknown' };
$c = .parent.name with $d;
$c = .<parent><name> with $d;

I'm not certain how you would add a fuse operator to Perl 6 as it requires disabling an entire expression in midstream, which is more of control flowey than even the trinary conditional operator (?:, or ??!! in Perl 6).

As for the ?&& operator, that one is defined in Perl 6 and is named "andthen". There is also the "orelse" operator.

# These two are equivalent
$a = ($d andthen $e);
$a = $d.defined ?? $e !! Nil; # Nil is like undef, ??!! is same as ?:

# These two are also equivalent
$a = ($d orelse $e);
$a = $d.defined $d ?? $d !! $e;

Leave a comment

About martin

user-pic Blogging about Mathematics, Programming Languages, Open-Source, Privacy, Security, Board Games, Bicycles, Classical Music, and more.