Skip to content

Commit

Permalink
Update behavior of augmented null coalescing operator
Browse files Browse the repository at this point in the history
x ??= y now behaves like x ?? (x = y) rather than x = x ?? y

These are equivalent when x is null.

When x is non-null, we avoid an unnecessary re-assignment step, which
also allows for the possibility of using this operator with immutable
hash tables without raising errors when keys already exist.

Closes: #3612
  • Loading branch information
d-torrance committed Dec 17, 2024
1 parent 890ccae commit 4bb2638
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 23 deletions.
46 changes: 24 additions & 22 deletions M2/Macaulay2/d/evaluate.d
Original file line number Diff line number Diff line change
Expand Up @@ -1236,16 +1236,37 @@ tryEval(c:Code):Expr := (
else tryEvalSuccess = true);
p);

nullify(c:Code):Expr := (
e := tryEval(c);
if tryEvalSuccess
then (
when e
is Nothing do e
else (
f := lookup(Class(e), QuestionQuestionS);
if f == nullE then e
else applyEE(f, e)))
else nullE);

nullCoalescion(lhs:Code,rhs:Code):Expr := (
e := nullify(lhs);
when e
is Nothing do eval(rhs)
else e);
setup(QuestionQuestionS, nullify, nullCoalescion);

augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
when lookup(x.oper.word, augmentedAssignmentOperatorTable)
is null do buildErrorPacket("unknown augmented assignment operator")
is s:Symbol do (
-- evaluate the left-hand side first
lexpr := nullE;
if s.word.name === "??" -- null coalescion; ignore errors
if s.word.name === "??" -- x ??= y is treated like x ?? (x = y)
then (
e := tryEval(x.lhs);
if tryEvalSuccess then lexpr = e)
e := nullify(x.lhs);
when e
is Nothing do nothing
else return e)
else lexpr = eval(x.lhs);
left := evaluatedCode(lexpr, dummyPosition);
when left.expr is e:Error do return Expr(e) else nothing;
Expand Down Expand Up @@ -2091,25 +2112,6 @@ export notFun(a:Expr):Expr := if a == True then False else if a == False then Tr
-- to evaluate methods in hashtables.dd before it is defined.
applyEEEpointer = applyEEE;

nullify(c:Code):Expr := (
e := tryEval(c);
if tryEvalSuccess
then (
when e
is Nothing do e
else (
f := lookup(Class(e), QuestionQuestionS);
if f == nullE then e
else applyEE(f, e)))
else nullE);

nullCoalescion(lhs:Code,rhs:Code):Expr := (
e := nullify(lhs);
when e
is Nothing do eval(rhs)
else e);
setup(QuestionQuestionS, nullify, nullCoalescion);

-- Local Variables:
-- compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d evaluate.o "
-- End:
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ doc ///
The null coalescing operator can be combined with
@TO "augmented assignment"@ as a shortcut for
@M2CODE "if x === null then x = y"@ and
@M2CODE "if not x#?i then x#i = y"@.
@M2CODE "if not x#?i then x#i = y"@. Note that it behaves
slightly differently than other augmented assignment operators,
as @CODE "x ??= y"@ is treated like @CODE "x ?? (x = y)"@ rather
than @CODE "x = x ?? y"@.
Example
x = null
x ??= 2
Expand Down
4 changes: 4 additions & 0 deletions M2/Macaulay2/tests/normal/augmented-assignment.m2
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,7 @@ assert BinaryOperation(symbol ===, y ?? x, y)
assert BinaryOperation(symbol ===, x ?? y, y)
x ??= y
assert BinaryOperation(symbol ===, x, y)

-- issue #3612
h = new HashTable from { symbol cache => new CacheTable };
h.cache ??= new CacheTable

0 comments on commit 4bb2638

Please sign in to comment.