2
$\begingroup$

Many languages extend their "indexing" operator . to a nil-coalescing variant ?. to deal with indexing chains such as a.b.c where the parents are optional, which can then be conveniently rewritten as a?.b?.c. This seems to be the main use case for nil-coalescing besides providing a default if something is nil, e.g. x ?? 42 (I'm using the Lua term nil rather than null here since I plan to extend Lua).

I'm considering generalizing this: All binary operators should support nil coalescion for both operands by putting a question mark on their respective sides. For example, you could write t?.x if t may be nil to short-circuit the expression to nil. t.?x on the other hand would short-circuit to nil if x is nil (this is not really necessary though since t[nil] is nil in Lua, given no metamethods).

This seems to be useful for arithmetic operations (e.g. c = a ?+? b could come in handy) and I would prefer not to impose arbitrary restrictions on which operators nil-coalesce. Relational operators could also benefit.

It is however pretty useless if not detrimental for the logic operators not, and and or: not? nil would evaluate to nil, which would be rather confusing, since we get a conflict between nil-coalescing operators short-circuiting and logic operators short-circuiting as well as conflicting behavior: nil ?or 1 would evaluate to nil, behaving like and rather than or.

(In a Lua extension), should nil-coalescing be restricted to indexing (and the "default" operator ??), where it is most useful? Which limits should be imposed on a generalization?

$\endgroup$
2
  • 2
    $\begingroup$ I've usually heard ?. referred to as optional-chaining, and only ?? is coalescing. $\endgroup$
    – Bbrk24
    Commented Jul 6, 2023 at 12:37
  • $\begingroup$ @Bbrk24 indeed it seems I have been using the word "coalescing" wrong. The [?]<op>[?] operators are supposed to be nil-short-circuiting rather than "coalescing"... $\endgroup$
    – Luatic
    Commented Jul 6, 2023 at 12:43

3 Answers 3

3
$\begingroup$

Cons

Adding symbols to transform operators is a rare practice, as far as I know another one is julia, which appends ..

It might look like this if extended down the road:

  • a + b: add
  • a .+ b: broadcast add
  • a ?.+ b: nullable broadcast add
  • a ?.+= b: nullable broadcast add assign
  • ......: some more horrible add

Syntax Sugar with Metatable Extension

In fact I think your definition is not "generalized" enough, I call this monadic call syntax.

When the value of the operation is a monad such as Option<T>, Result<T, E>, ParseState<R>, etc.

? means and_then, such as:

object?:invoke()
-- object:and_then(function(inner) return inner.invoke() end)

object?[index]
-- object.and_then(function(inner) return inner[index] end)

object ?= new
-- object.and_then(function(inner) inner = new end)

?? means or_else, such as:

object ?? default
-- object:or_else(function() return default end)

Of course lua does not have generics, I think you can add __and_then and __or_else in the metatable as a duck type interface.


I prefer a! or b to a ?or b, where ! means assert non-nil, maybe you can add __assert as duck type interface.

In general it's up to you, supporting a ?- b, ?- a, a ?~= b etc. doesn't make much difference in implementation.

And I think this method is more suitable for converting your extension language to the standard lua language.

$\endgroup$
4
  • $\begingroup$ I had not considered extending this to assignment (which is a statement in Lua, though I'm considering changing that); I would of course extend it to also support calls and indexing operations, though I probably wouldn't add new metamethods. I wasn't aware of broadcast add either. Assignment operators will be a follow up question :) $\endgroup$
    – Luatic
    Commented Jul 6, 2023 at 12:10
  • $\begingroup$ Your "and-then" assignment does exist in Swift, but the spacing is different: object? = new. $\endgroup$
    – Bbrk24
    Commented Jul 6, 2023 at 12:38
  • $\begingroup$ @Bbrk24 I suddenly remembered that swift also have combined operators, &+ indicates overflow add. $\endgroup$
    – Aster
    Commented Jul 6, 2023 at 12:45
  • $\begingroup$ That mom said call syntax is very interesting, I just might use it in my language đź‘€ $\endgroup$
    – Seggan
    Commented Jul 7, 2023 at 0:55
2
$\begingroup$

I think the best argument against such operators is that they would make control-flow harder to read, due to short-circuiting.

An optional chaining operator like Javascript's is short-circuiting, meaning that if the left operand is nil, the rest of the expression is not evaluated. For example, in obj?.x.y, the property access .y won't be evaluated if obj is nullish. This makes sense for property access, because of course you wouldn't want to evaluate nil.y in the case where you know the left operand is nil. It's also relatively readable, because property accesses have a very high precedence and few ways for the expression to continue; and obj?.x?.y would have a different meaning, since it doesn't fail if obj is non-nil but obj.x is nil.

But for an operator like + or - with a much lower precedence, the same rule could have surprising consequences:

let result1 = a ?- b + c()
let result2 = if a ?> 0 then d() else e() end

(Yes, Lua doesn't have ternary expressions, but other languages do.)

In the above example, it would be quite surprising if c() were evaluated when a is nil, because it is "clearly" part of the rest of the expression, even though it is not a descendant of the ?- node in the parse tree. On the other hand, it would be very surprising if neither d() nor e() were evaluated when a is nil, even though a similar logic applies.

All of this said, you could say that operators like ?+ or ?+? which coalesce on the left operand aren't short-circuiting, but then either this would be inconsistent with the rule for ?., or you would have to forego short-circuiting entirely in which case your ?. operator would have different behaviour to what users are familiar with from other languages.


One more reason to not have operators like ?+? is that they are hard to search for. Operators like this will be rarely used, so they will often be encountered by users who haven't seen them before. But existing search engines like Google or Stack Overflow's search don't do a good job of searching for punctuation like this.

$\endgroup$
1
$\begingroup$

This could be done better using exceptions. You could define an operator to catch nil exceptions non-recursively and return another value instead, possibly the nil value, possibly in a grammar similar to @ in PHP.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .