How to disallow the application of functions outside a white list? - Mathematica Stack Exchange most recent 30 from mathematica.stackexchange.com 2019-08-21T23:40:52Z https://mathematica.stackexchange.com/feeds/question/139842 http://www.creativecommons.org/licenses/by-sa/3.0/rdf https://mathematica.stackexchange.com/q/139842 7 How to disallow the application of functions outside a white list? kjo https://mathematica.stackexchange.com/users/2464 2017-03-11T22:01:58Z 2017-03-12T15:37:16Z <p>In an <a href="https://mathematica.stackexchange.com/q/139818/2464">earlier post</a> I described a special kind of list (actually, a special kind of <em>multiset</em>) that can be represented more efficiently with a structure like this:</p> <pre><code>dd = discreteData[&lt;|"scale" -&gt; 1.234, "bias" -&gt; 5.678, "tally" -&gt; {{-5, 2}, {-4, 251}, {-3, 5941}, {-2, 60383}, {-1, 241185}, { 0, 383613}, { 1, 241644}, { 2, 61035}, { 3, 5686}, { 4, 259}, { 5, 1}}|&gt;]; </code></pre> <p>(Basically, the values encoded by this data structure are all of the form \$kS + B\$, where \$k\$ is an integer, and \$S\$ and \$B\$ are the numbers that the data structure labels <code>"scale"</code> and <code>"bias"</code>. The <code>"tally"</code> item is a list of pairs \$\{k, n\}\$, where \$n\$ is the number of times that the value \$kS + B\$ appears in the data.)</p> <p>Now, let</p> <pre><code>dd2list[dd_] := dd[[1, "bias"]] + dd[[1, "scale"]] Flatten[ ConstantArray @@@ dd[[1, "tally"]] ]; </code></pre> <p>Since any <code>discreteData</code> object <code>dd</code> contains the same information as the corresponding <code>List</code> object <code>dd2list[dd]</code>, it would be <em>great</em> if one could arrange matters such that <code>dd</code> and <code>dd2list[dd]</code> could be used interchangeably in <em>Mathematica</em> programs.</p> <p>By defining suitable upvalues, one can have <code>dd</code> behave like <code>dd2list[dd]</code> in the specified context. E.g.</p> <pre><code>discreteData /: Length[ d_discreteData ] := With[ { assoc = d[] }, Plus @@ ( Last /@ assoc[["tally"]] ) ]; Length[dd] == Length[dd2list[dd]] (* True *) </code></pre> <p>Unfortunately, this one upvalue covers only a tiny fraction of the expressions one is likely to find <code>dd</code> in. If one mistakenly replaces a list object with its <code>discreteData</code> counterpart in an expression for which no proper upvalue has been defined, the results could change dramatically (and for the worse, of course).</p> <p>For example, if <code>dd</code> is defined as shown above, the evaluation of <code>Sqrt /@ dd</code> succeeds without warning, and the result even has the correct "shape" (i.e. the right head, the right fields, with values also correctly dimensioned), but it is <em>grossly</em> incorrect.</p> <p>Of course, one can avoid this one error by defining a suitable upvalue for <code>Map</code>, but in order to make this data structure resistant to such silent errors <em>in general</em>, one would have to implement upvalues for a huge number of list-consuming functions. This does not seem practical to me.</p> <p>In order to make such a data structure behave like a List <em>safely</em>, one needs at least one of</p> <ol> <li><p>the specification of a complete list interface;</p></li> <li><p>a way to make it a visible error to apply to objects of the new class any function that is not included in a white-list of approved functions.</p></li> </ol> <p>(<strong>NB:</strong> the white-list mentioned in 2 consists of precisely those functions for which I have defined upvalues.)</p> <p>Please correct me if I'm wrong, but, AFAIK, <em>Mathematica</em> has nothing like 1.</p> <p>This leaves 2. Is there a way to implement this?</p> <hr> <p><strong>Clarification</strong>: option 2 will <em>not</em> solve the problem of making <code>discreteData</code> behave like a list in any appropriate context; it only solves the problem of silent errors.</p> https://mathematica.stackexchange.com/questions/139842/-/139845#139845 4 Answer by b3m2a1 for How to disallow the application of functions outside a white list? b3m2a1 https://mathematica.stackexchange.com/users/38205 2017-03-11T22:45:11Z 2017-03-11T22:53:58Z <p>Here's a sample implementation of 2 although I think simply writing a generalizable list-like interface is probably the safer route:</p> <pre><code>whitelisted = Association@ Thread[ Thread@ Hold@ { Hold, HoldPattern, Print } -&gt; Null]; discreteData::unknown = "Result of applying function `` to discreteData object is unknown"; discreteData /: HoldPattern[ func_?(Function[Null, ! KeyMemberQ[whitelisted, Hold[#]], HoldAllComplete])[d_discreteData] ] := ( Message[discreteData::unknown, HoldForm[func]]; d ); discreteData /: HoldPattern[ func_?(Function[Null, ! KeyMemberQ[whitelisted, Hold[#]], HoldAllComplete])[a___, d_discreteData, b___] ] := ( Message[discreteData::unknown, HoldForm[func]]; HoldComplete[func[a, d, b]] ); </code></pre> <p>Then:</p> <pre><code>In:= List[discreteData] During evaluation of In:= discreteData::unknown: Result of applying function List to discreteData object is unknown Out= discreteData </code></pre> <p>But </p> <pre><code>In:= Hold@discreteData Out= Hold[discreteData] </code></pre> <p>And including Simon Wood's example now:</p> <pre><code>In:= discreteData + 1 During evaluation of In:= discreteData::unknown: Result of applying function Plus to discreteData object is unknown Out= HoldComplete[1 + discreteData] </code></pre> https://mathematica.stackexchange.com/questions/139842/-/139846#139846 4 Answer by Edmund for How to disallow the application of functions outside a white list? Edmund https://mathematica.stackexchange.com/users/19542 2017-03-11T22:58:28Z 2017-03-11T22:58:28Z <p>You may continue to use <code>UpValues</code> and construct a pattern that raises a <code>Message</code> when a function not in the white list is called.</p> <p>First create a <code>Message</code> to raise.</p> <pre><code>discreteData::undefsym = "discreteData not defined for `1`."; </code></pre> <p><code>UpValue</code> pattern to raise the message when a non-white list function is used. </p> <pre><code>discreteData /: f_[___, d_discreteData, ___] /; MemberQ[{Normal, Format, Length, Part, Set, If, List}, f] == False := Message[discreteData::undefsym, f] </code></pre> <p>White list functions work as expected and others will not.</p> <pre><code>Sqrt /@ dd </code></pre> <blockquote> <p><em>discreteData::undefsym: discreteData not defined for Map.</em></p> </blockquote> <p>Note that this will work because <code>discreteData</code> is sufficiently deep in <code>Map</code> and defined for <code>Normal</code> when it is entered as a parameter to <code>Normal</code>.</p> <pre><code>Normal /@ {dd, dd} Join[{dd}, {dd}] </code></pre> <p>This will not work and errors on <code>Sqrt</code> since <code>discreteData</code> is not defined for <code>Sqrt</code> when it is entered as a parameter to <code>Sqrt</code>.</p> <pre><code>Sqrt /@ {dd, dd} Join[dd, dd] </code></pre> <p>Hope this helps.</p> https://mathematica.stackexchange.com/questions/139842/-/139852#139852 4 Answer by Mr.Wizard for How to disallow the application of functions outside a white list? Mr.Wizard https://mathematica.stackexchange.com/users/121 2017-03-12T02:55:05Z 2017-03-12T15:37:16Z <p>Since, to the best of my knowledge, is not possible to program a custom interface that is integrated at the level of atomic objects such as a <a href="http://reference.wolfram.com/language/ref/SparseArray.html" rel="nofollow noreferrer"><code>SparseArray</code></a> I think some compromise is unavoidable.</p> <p>Edmund's method is the first one that came to mind, and I voted for it. It may still be your best option.</p> <p>However it is not <em>robust</em> in the manner I think you are seeking. Consider for example:</p> <pre><code>expr = Hold[{1, discreteData[x, y, {z1, z2, z3}], 3}]; Apply[foo, expr, {2}] Apply[foo, expr, {3}] Map[foo, expr, {3}] </code></pre> <blockquote> <pre><code>Hold[{1, foo[x, y, {z1, z2, z3}], 3}] Hold[{1, discreteData[x, y, foo[z1, z2, z3]], 3}] Hold[{1, discreteData[foo[x], foo[y], foo[{z1, z2, z3}]], 3}] </code></pre> </blockquote> <p>I imagine that none of these are the result you would <em>hope</em> to see from each operation.</p> <p>Perhaps it would be better for these functions to <em>ignore</em> your construct rather than mutating it errantly.</p> <hr> <h1>Proposal</h1> <p>One <em>compromise</em> to achieve this is to make a separate definition for each Symbol to which a data set is assigned.</p> <ul> <li><p>To do this I shall use a public function <code>setDiscreteData</code> and a global variable <code>\$DD</code></p></li> <li><p>All your UpSets should be given in the body of <code>setDiscreteData</code></p></li> </ul> <p>&nbsp;</p> <pre><code>Attributes[setDiscreteData] = HoldFirst; setDiscreteData[s_Symbol, rhs_] := With[{a := \$DD[s]}, a = rhs; s=. ^:= (ClearAll[s]; a=.); Set[s, new_] ^:= a = new; Length[s] ^:= a // Query["tally", Tr, Last]; ] </code></pre> <p>Now you would define a data set like this:</p> <pre><code>setDiscreteData[dd, &lt;|"scale" -&gt; 1.234, "bias" -&gt; 5.678, "tally" -&gt; {{-5, 2}, {-4, 251}, {-3, 5941}, {-2, 60383}, {-1, 241185}, {0, 383613}, {1, 241644}, {2, 61035}, {3, 5686}, {4, 259}, {5, 1}}|&gt; ]; </code></pre> <p>The defined rules apply:</p> <pre><code>Length[dd] </code></pre> <blockquote> <pre><code>1000000 </code></pre> </blockquote> <p>Every other operation sees <code>dd</code> as an atomic Symbol and handles it as such:</p> <pre><code>expr = Hold[{1, dd, 3}]; Apply[foo, expr, {2}] Apply[foo, expr, {3}] Map[foo, expr, {3}] </code></pre> <blockquote> <pre><code>Hold[{1, dd, 3}] Hold[{1, dd, 3}] Hold[{1, dd, 3}] </code></pre> </blockquote> <pre><code>Replace[dd, x_ :&gt; foo, 1] dd + 8 </code></pre> <blockquote> <pre><code>dd 8 + dd </code></pre> </blockquote> <p>Once a data set Symbol (<code>dd</code>) has been assigned it can be reassigned with <code>Set</code>:</p> <pre><code>dd = &lt;|"scale" -&gt; 1.234, "bias" -&gt; 5.678, "tally" -&gt; {{-1, 2}, {0, 7}, {1, 4}}|&gt;; Length[dd] </code></pre> <blockquote> <pre><code>13 </code></pre> </blockquote> <p>The Symbol may be reset to normal (all definitions cleared) using <a href="http://reference.wolfram.com/language/ref/Unset.html" rel="nofollow noreferrer"><code>Unset</code></a>:</p> <pre><code>dd=. </code></pre> <hr> <p>This method is incidentally related to <a href="https://mathematica.stackexchange.com/q/7214/121#7273">How to Set parts of indexed lists?</a></p>