Services & Resources / Wolfram Forums / MathGroup Archive
-----

MathGroup Archive 2007

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Need features to make kernel and user functions bullet proof

  • To: mathgroup at smc.vnet.net
  • Subject: [mg84064] Re: Need features to make kernel and user functions bullet proof
  • From: Szabolcs Horvát <szhorvat at gmail.com>
  • Date: Sat, 8 Dec 2007 05:46:00 -0500 (EST)
  • References: <fjbceq$4mf$1@smc.vnet.net>

Hi,

Here are my thoughts on this:

Ted Ersek wrote:
> Need features to make kernel and user functions bullet proofWe can
> overload functions that are part of the Mathematica language, and this
> is good.  However, we often hear in the MathGroup that you can easily
> break the kernel by changing kernel functions. This wouldn't be so risky
> if users could not indirectly change the way functions such as Solve,
> Simplify, NDSolve, etc. perform. I mean a user should be able to change
> what Simplify does by making a new definition for Simplify, but changing
> functions such as Thread, Map, or Apply should not change Solve, Simplify,
> or NDSolve. Besides that users who are writing their own functions should
> be able to ensure their functions cannot be broken inadvertently.

But is this really necessary?  I mean, is there any legitimate reason to 
change symbols like Thread, Map, or Apply?  If one needs a Map that 
behaves a bit differently, shouldn't one just define a new myMap function?

(I know that some people are changing these symbols ... see e.g.

http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/90da91b2fb43891a/4d26e4c1e1edcce3#4d26e4c1e1edcce3

But I always found these things extremely scary (and I think that it is 
a bad practice to do this).  As you mentioned, it is possible that some 
built-in (or package) function uses the form Map[{a,b,c},d] (see the 
referenced post), so it would be broken by this change to Map.  However, 
in this situation the user could have just defined his own listableMap[] 
function, which would be safe.  Actually after I read that message I 
searched all the standard packages and the UpValues, OwnValues and 
DownValues of symbols returned by Names[] for Map[_List, __], but found 
nothing (though I'm not sure I searched the *built-in* definitions 
correctly, and I could completely prevent evaluation of the symbols only 
if I ran Mathematica without the Front End ...) )

> 
> -------------------------------------------------------------------------
> Also notice you can change the default options of a protected symbol
> without first calling Unprotect. As a result a user can seriously break
> a lot of functions with lines such as
> 
>   SetOptions[ReplaceRepeated,MaxIterations -> 1];
>   SetOptions[Map,Heads->True];
> 
> Mathematica needs a new attribute called OptionsProtected.
> OptionsProtected::usage="OptionsProtected is an attribute that prevents
> the default options of a symbol from being modified."


OptionsProtected could be useful.


> -------------------------------------------------------------------------
> 
> Now about preventing inadvertent changes to a functions definitions. One
> way WRI could provide this capability would be to have all symbols in the
> System` context also in a context called DefaultDefinitions`. All Symbols
> in context DefaultDefinitions` would have attributes {OptionsProtected,
> Protected, Locked}.  That would make it impossible for a user to modify
> features in the DefaultDefinitions` context.
> 
> Things that happen automatically behind the scenes should use symbols
> from the DefaultDefinitions` context to ensure everything always
> works correctly.  So for example when definitions are automatically
> added to DownValues[f], UpValues[f], etc.  they should be created using
> DefaultDefinitions`HoldPattern, and DefaultDefinitions`RuleDelayed.
> 
> Imagine what could happen if you changed the meaning of RuleDelayed in
> the current Mathematica.
> 
> -------------------------------------------------------------------------=
> -
> WRI could also give us a new function called SystemDefaults that would 
> do the following.
> 
> Attributes[SystemDefaults]={HoldRest};
> 
> SystemDefaults[All, expr] replaces all symbols in expr that are in
> the System` context with the same symbol from the DefaultDefinitions`
> context. Once the replacements are made the new form of expr evaluates.
> 
> SystemDefaullts[{symb1, symb2, ...},expr]  selects from {symb1, symb2,
> ....} Symbols in the System` context and replaces each instance of
> these symbols in expr with the same symbol from the DefaultDefinitions`
> context. Once the replacements are made the new form of expr evaluates
> ..
> 
> -----------------------------------------------
> 
> Then I could write the following and ensure all system symbols below
> are from the DefaultDefinitions` context and it would not be possible
> to make Inadvertent changes to my function integrate.
> 
> 
> 
> SystemDefaults[All,
>   integrate[c_?NumericQ, f_, x_Symbol]:=c*integrate[f,x];
>   integrate[f_+g_,x_Symbol]:=integrate[f,x]+integrate[g,x];
>   integrate[x_Symbol,x_Symbol]:=1/2 x^2;
>   integrate[_Symbol^n_?(NumericQ[#]&&TrueQ[# != -1]&), 
> x_Symbol]:=1/(n+1) x^(n+1)
> ]
> 
> ------------------------------------------------
> Instead I might want to use the following to ensure
> symbols DefaultDefinitions`NumericQ, DefaultDefinitions`Power,
> DefaultDefinitions`SetDelayed are used in my definitions of integrate. No
> other symbol form the DefaultDefinitions` context would be used.
> 
> 
> 
> SystemDefaults[{NumericQ,Power,SetDelayed},
>   integrate[c_?NumericQ f_,x_Symbol]:=c*integrate[f,x];
>   integrate[f_+g_,x_Symbol]:=integrate[f,x]+integrate[g,x];
>   integrate[x_Symbol,x_Symbol]:=1/2 x^2;
>   integrate[_Symbol^n_?(NumericQ[#]&&TrueQ[#!=-1]&),x_Symbol]:=1/(n+1)x
> ^(n+1)
> ]
> 
> In the above examples above use of SystemDefaults[_._] would not force
> the use of symbols from the DefaultDefinitions` context when f[t], or
> (t) in integrate[f[t], t] evaluates.
> 

Now about changing symbols like Simplify, NIntegrate, etc.:

There might be reasons for changing these, but one could argue that a 
legitimate change should extend these symbols in a mathematically 
sensible way, so that other function which use them would not break 
(i.e. Simplify would still simplify expression, but would be a bit 
smarter, NIntegrate would still compute integrals numerically, possibly 
with another method etc.)  If the behaviour of these functions is 
changed completely, then probably it would have been better to define 
completely new functions instead.

So I think that one should never change built-in symbols, except in the 
case when it is really expected that the new definitions would be 
applied even when the re-defined symbols are used inside some package 
(or built-in) function.

I see that the proposed SystemDefaults allows for excluding certain 
symbols (more precisely: it allows for only including certain symbols 
when rewriting them to the alternatives in DefaultDefinitions), but this 
couldn't work correctly because of the nature of Mathematica:  Suppose 
that we're writing a new function that uses Simplify, and we want to 
allow for the re-definition of Simplify, therefore we use 
System`Simplify instead of DefaultDefinitions`Simplify.  But if our 
function "calls" a built-in function that also uses Simplify (I don't 
know if there are actually any of these), the built-in function will use 
DefaultDefinitions`Simplify and not the intended System`Simplify.  So we 
would end up with two different versions of Simplify being 
inconsistently called in different situations.  This sounds even more 
dangerous and confusing than the current situation.

In my (subjective) opinion, for those functions where re-definition 
seems legitimate (like Simplify), WRI should provide a documented and 
safe API that only allows sensible changes.  And indeed they do for some 
functions:  Simplify has the TransformationFunctions option, NIntegrate 
can be extended with new integration methods, NumericQ and Derivative 
are directly assignable (NumericQ[n] = True and Derivative[1][f] = g 
work without Unprotect[]ing these symbols), etc.

To summarize:  I think that one should never modify any Protected 
built-in functions, except through documented APIs.  But I like the idea 
of OptionsProtected.

What do other MathGroup users think about this?  Are there situations 
when it is legitimate to modify built-ins?

I can give one example and show that it was a bad idea because it really 
breaks things:  when I was trying to get the DownValues of all symbols 
returned by Names[], I changed its HoldAll attribute to HoldAllComplete 
so that it would work on really strange symbols like 'x' if 'x' has the 
definitions x[1]=1 and _[x] ^= x.  (Question: Are there other ways of 
doing this than changing the attributes of DownValues?)  But with this 
change, DownValues[Evaluate[sym]] ceases to work, and indeed I got lots 
of error messages when I tried to open the Help Browser ...

Szabolcs

P.S.  I hope that my broken English didn't make this long post 
completely unintelligible.

P.P.S.  Your Mathematica Tricks website is really nice!


  • Prev by Date: line in 3d in direction of eigen vector
  • Next by Date: Memory leak bug in plotting functions? (Sudden kernel shutdown without error message)
  • Previous by thread: Need features to make kernel and user functions bullet proof
  • Next by thread: Re: Need features to make kernel and user functions bullet proof