M2 icon indicating copy to clipboard operation
M2 copied to clipboard

new assignment operator

Open DanGrayson opened this issue 5 years ago • 2 comments
trafficstars

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

DanGrayson avatar Nov 16 '20 18:11 DanGrayson

This would be really handy for caching things if foo in the example above was lazily computed.

mahrud avatar Dec 21 '21 14:12 mahrud

Right -- that's what "and otherwise evaluates the code foo" means.

DanGrayson avatar Dec 21 '21 18:12 DanGrayson

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?

mahrud avatar Jan 24 '24 07:01 mahrud

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.

d-torrance avatar Jan 24 '24 13:01 d-torrance

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.

d-torrance avatar Jan 24 '24 13:01 d-torrance

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.

d-torrance avatar Jan 24 '24 15:01 d-torrance

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)

mahrud avatar Jan 24 '24 16:01 mahrud

Yes, exactly!

d-torrance avatar Jan 24 '24 17:01 d-torrance