From 4bb2638756eebe8b5da1a885b5a17255184e6b98 Mon Sep 17 00:00:00 2001 From: Doug Torrance Date: Tue, 17 Dec 2024 07:45:25 -0500 Subject: [PATCH] Update behavior of augmented null coalescing operator 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 --- M2/Macaulay2/d/evaluate.d | 46 ++++++++++--------- .../Macaulay2Doc/doc_augmented_assignment.m2 | 5 +- .../tests/normal/augmented-assignment.m2 | 4 ++ 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/M2/Macaulay2/d/evaluate.d b/M2/Macaulay2/d/evaluate.d index 8c4629f4cbe..503dc262b66 100644 --- a/M2/Macaulay2/d/evaluate.d +++ b/M2/Macaulay2/d/evaluate.d @@ -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; @@ -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: diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_augmented_assignment.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_augmented_assignment.m2 index 963572217d1..f782164e4b8 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_augmented_assignment.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_augmented_assignment.m2 @@ -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 diff --git a/M2/Macaulay2/tests/normal/augmented-assignment.m2 b/M2/Macaulay2/tests/normal/augmented-assignment.m2 index 40532d68c56..d2255a056db 100644 --- a/M2/Macaulay2/tests/normal/augmented-assignment.m2 +++ b/M2/Macaulay2/tests/normal/augmented-assignment.m2 @@ -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