It definitely takes slightly different thinking to deal with
mutability v immutability. One way to do this is to have immutable
types plus a calculator object which supports an API for working on
mutable values. When you ask the calculator for a value, it would
make an immutable copy. There are probably also useful patterns from
the Java community that would apply here since it has the same
issues with all complex types being pass-by-reference.

The performance problem with your approach is that it would require
attempting to invoke the metamethods on every parameter ever passed
and every value ever assigned.