M2
M2 copied to clipboard
new assignment operator
In discussions with Mike, it has occurred to us that it might be nice to introduce the syntax
X # key ?= foo
to denote an expression whose value is X#key if the mutable hash table (or mutable list) X has a value stored under that key, and otherwise evaluates the code foo, stores the resulting value under that key, and returns that value.
The current alternative coding is if X#?key then X#key else X#key = foo, which is more verbose.
@mikestillman
This would be really handy for caching things if foo in the example above was lazily computed.
Right -- that's what "and otherwise evaluates the code foo" means.
Here's an alternative syntax suggestion. We currently have global assignment with = and local declaration with := for variables, but for hash tables only = is defined:
i1 : X = new MutableHashTable from {};
i2 : X.cache := 12
stdio:3:9:(3): error: can't assign a method for this binary operator
i3 : X#"key" := 12
stdio:4:9:(3): error: can't assign a method for this binary operator
Why not use X#key := foo and X.cache := foo for this new operator instead?
I like this idea a lot, especially for the lazy evaluation!
I think I prefer ?= over := since it suggests the call to ?# or ?., and also avoids overloading := even more. (I remember getting confused back in grad school by := meaning both local assignment and method installation.)
Related question: I wonder if adding an ?_ operator would be helpful for user-defined classes? Then potentially this proposed operator could work there as well.
Related: https://en.wikipedia.org/wiki/Null_coalescing_operator
It seems some other languages use ??= for this. It may also be useful to add ??, i.e., x ?? y returns x unless it's null or raises an error, in which case we return y.
Adding a null coalescing operator is relatively simple:
--- a/M2/Macaulay2/d/actors.d
+++ b/M2/Macaulay2/d/actors.d
@@ -1164,6 +1164,14 @@ subvalueQ(lhs:Code,rhs:Code):Expr := (
else subvalueQ(left,right)));
setup(SharpQuestionS,subvalueQ);
+nullCoalescion(lhs:Code,rhs:Code):Expr := (
+ left := eval(lhs);
+ when left
+ is Error do eval(rhs)
+ is Nothing do eval(rhs)
+ else left);
+setup(QuestionQuestionS, nullCoalescion);
+
isFinite(e:Expr):Expr := (
-- # typical value: isFinite, Number, Boolean
when e
Then we get:
i1 : 2 ?? 3
o1 = 2
i2 : null ?? 3
o2 = 3
i3 : 1/0 ?? 3
stdio:3:2:(3): error: division by zero
o3 = 3
With some minor changes to #3079 (in particular, not evaluating the right hand side right away), ??= would then work out of the box. It might be good to suppress the error message, though.
So to be clear, you could use ?? like:
strategy := opts.Strategy ?? Default;
or for caching like:
dim Ring := ZZ => R -> R.dim ??= ( ... compute dimension ... )
or
basis(ZZ, Module) := Matrix => (n, M) -> M.cache#(basis, n) ??= (
... compute basis ... )
and the augmented version of ?? would work for any type of variable, not just mutable hashtables/lists? e.g. would something like this work?
isom := last isIsomorphic(M, N);
isom ??= try inducedMap(M, N) else error "no natural map found"
(the point being that if M and N are isomorphic we use the isomorphism, otherwise try to find a natural map between them and if not signal an error)
Yes, exactly!