So, I had a look at that old version of my notebook and I found it too specific. I copy here part of what was the documentation of my Override.m package. It's more general. (I'll spare you the code required to add options...)
Here we go.
It's easy to redefine a built-in procedure so that it can call itself without incurring in an infinite recursion. The key idea is to condition the new definition by means of variable, say $override
, that prevents built-in evaluation and diverts the call to user defined code. If this code needs to call the built-in version of the procedure, all it has to do is to change the value of the variable $override
so that it won't call itself. In a way the use of this variable is analogous to specifying the prefix 'super' that is used in Java programming to refer to a method of a superclass from within an overriding method with the same signature.
Here, the value of $override
is initialized to True
in order to prevent Mathematica from taking over the user defined code. Protecting the variable is a good idea.
$override = True;
Protect[$override];
Now, whenever we want to redefine a built-in procedure, let's call it BuiltinProc
, in term of itself we just define it with a conditioned left hand side (so that the definition applies only when $override = True
) and enclose the portion of code that calls the original procedure in a Block
that sets $override = False
. Better yet we can call, from inside the Block
, our own code that can make use of the built-in procedure as we would in a fresh Mathematica kernel. Let's call userProc
the user defined code:
Unprotect[BuiltinProc];
BuiltinProc[argsandopts___ ] /; $override == True :=
Block[
{$override = False},
userProc[argsandopts]
];
Protect[BuiltinProc];
(Unprotecting and Protecting are needed since the built-in symbols are by default protected). Note that when the argument sequence argsandopts
is the same as for the built-in procedure (as is the case with such a general pattern matching scheme) we are in fact redefining it.
Once we've done this, we can write the code for userProc
and allow it to call the built-in procedure at will: since it's called inside a Block
that sets $override=False
we do not need to worry about the value of $override
inside the code. Calling BuiltinProc
in the global environment (where $override
is True
) will result in a call to userProc
and in a (possible) call to BuiltinProc
inside a Block
where $override
is False
. There is no danger to incur in an infinite recursion since the two calls to BuiltinProc
are in fact different and complementary.
This is a sample code that adds to the previous snippets and shows that whenever userProc
is called it will use the built-in procedure whose arguments and options have been suitably processed before being passed to it:
userProc[args___, opts___Rule] :=
(
Print["This code was called from a Block
that temporarily set $override to ", $override, ". \nThe following \
function contains a call to the original BuiltinProc without conditions." ];
oldargs = FA[args]; oldopts = FO[opts];
myFun[
BuiltinProc[oldargs, oldopts],
otherargsandopts
]
)
When we call BuiltinProc
in a global environment where $override=True
we are in fact calling userProc
that, in turn, can use the built-in code of the original built-in procedure:
BuiltinProc[x, y, {a, b}, myopt -> Automatic]
This code was called from a Block that temporarily set $override
to False
.
The following function contains a call to the original BuiltinProc
without
conditions.
myFun(BuiltinProc(FA(x, y, {a, b}), FO(myopt -> Automatic)),
otherargsandopts)
Example: Fourier rescaled
Let's try it out. We redefine Fourier
in order to divide its result by the square root of the length of the list passed to it. This is the result of the built-in procedure Fourier
in a fresh Mathematica kernel:
Fourier[{1, 2, 3, 4}] // Chop
{5., -1. - 1. I, -1., -1. + 1. I}
If we redefine the procedure in a way that it can call itself:
$override = True;
Unprotect[Fourier];
Fourier[data_List ] /; $override == True := Block[{$override = False},
Fourier[data]/Sqrt[Length[data]]
];
Protect[Fourier];
We can have a new version of Fourier
that works according to our taste (on version 4 we should consider the option for FourierParameters
, while on previous versions the new definition can be used to add exactly that option).
Fourier[{1, 2, 3, 4}] // Chop
{2.5, -0.5 - 0.5 I, -0.5, -0.5 + 0.5 I}
Tah dah!
To go back to 'factory settings' we just clear our definition
Unprotect[Fourier]; Clear[Fourier]; Protect[Fourier];
The following conditional assignment allows for another kind of unsetting, since it is possible to use the form of the left hand side to choose the assignment we did (this one come from Villegas' tutorial on working with unevaluated expressions - I wish I had read it before creating my packages)
Unprotect[Fourier];
Unset[Fourier[data_List] /; $override == True];
Protect[Fourier];
Expectation
. Could you not write your own function to callmyExpectation[expr_]
when desired andExpectation
otherwise? $\endgroup$Unprotect
to modify the definition and indeed, had submitted that as my answer to my own question. But upon testing that method further --- even if I work it inside aModule
, hoping that variable scoping would limit the effects ofUnprotect
(it didn't) --- my approach was behaving very unexpectedly. In the end, I ended up doing something what you'd suggested. Thanks for the thought! $\endgroup$Expectation
is inefficient? If you can give a relatively clear example we might be able to improve the built-in methods! $\endgroup$