Defining UpValues

34 views
Skip to first unread message

István Zachar

unread,
Jul 5, 2010, 9:14:20 PM7/5/10
to
Dear Group,

consider the following code:

func[sys_Integer] := sys;
func /: func[sys_Integer][args___] := sys + 1;
func[sys_Function] := sys;

{
func[1],
func[1][2],
func[# + 2 &],
func[# + 2 &][2]
}

Is there a way to define func such a way that when the main argument
is an Integer, any further arguments are ignored? That is, to
return: {1, 2, #1 + 2 &, 4} instead of {1, 1[2], #1 + 2 &,
4}. At present, the second function definition is basically ignored by
the kernel.

I am aware that func[sys_type, args___] would be the easiest way to
use here, but in my real code
1. there is a large number of named options supplied with sys
preventing (or at least making hard) the use of *any number* of
optional second arguments;
2. sometimes the main call (func[sys]) is separated from the
introduction of any optional argument (this is part of a GUI), which
may cause calls like this:
x = func[some_function]; ... (* calculations involving x *) ... ;
y = x[2];

Thanks in advance,

Istv=E1n


Leonid Shifrin

unread,
Jul 7, 2010, 7:39:41 AM7/7/10
to
Istvan,

Actually in that first letter that I never sent to you I had a solution
which used a wrapper,
so this is how I also would do that. The main problem to overcome in making
your original code work
was that the heads of expressions (even composite heads) evaluate before
anything else (there is a separate
recursion on heads in the case of nested heads), and evaluation of func[sys]
in func[sys][args] is
completely local in the sense that at the time func[sys] is evaluated, the
evaluator has no idea
whether this is a head of a larger expression or a stand-alone expression.

I tried many different things but all of them failed in one way or another,
until I realized that the evaluation stack can provide the non-local
evaluation information
that we needed. It looks like this trick can be used generally when we need
to divert evaluation
in a way that can not be achieved by the standard evaluation control
(Evaluate, Unevaluated,
Hold-attributes, etc), but one has to have a really good reason to do so!

Best,
Leonid

2010/7/6 Istv=E1n Zachar <replic...@gmail.com>

> Dear Leonid,
>
> thanks for the answer. Yes, I agree that the design is not the best in this
> case, and to tell you the truth,
> just after posting my question here I've decided - though I was a bit
> dubious about it - to introduce the wrapper.
> Now I'm much more confident with my choice as it seems to be the general
> method to handle situations like this.
>
> The only problem (at the moment) with the wrapper is that previously the
> (IRL) outcome of func was a list, while
> now it has the wrapper as head, making ReplaceAll calls a bit harder to
> implement (like
>
> {a, b} /. Wrapper[a -> 1, b -> 2]
>
> which gives an errormessage. But setting UpValues for Wrapper with
> ReplaceAll does help here:
>
> Wrapper/: ReplaceAll[expr_, rep_Wrapper] :== ReplaceAll[expr, List @@ rep];
>
> Anyway, your solution is impressive in its simplicity: I never would have
> thought to fiddle with the evaluation stack,
> but it's a very clever workaround! Though I won't use it, it still remains
> to be a rather tricky intellectual achievement : )
>
> Istvan
>
>
>
> On 2010.07.06. 14:12, Leonid Shifrin wrote:
>
> Hi Istvan,
>
> This was a tough one. Here is one way (it took me a while to figure it out,
> first I thought it was
> not possible, and almost sent you a long explanation why it is
> impossible))):
>
> ClearAll[func];
> func[x_Integer] :==
> x /; ! MemberQ[Stack[_], HoldForm[func[_Integer][args___]]];
> func /: func[sys_Integer][args___] :== sys + 1;
> func[sys_Function] :== sys;
>
>
> In[191]:== {func[1], func[1][2], func[# + 2 &], func[# + 2 &][2]}
>
>
> Out[191]== {1, 2, #1 + 2 &, 4}
>
> I would still consider changing the design though, since this kind of
> difficulties / workarounds usually
> indicate to me that the design of the function in question is not well
> thought over. For example, if
> your sys is often supplied with additional optional arguments, you can
> create a wrapper say sysData (or, just
> a List) and store them there, like func[sysData[sys,opts___]], etc.
>
> Hope this helps.
>
> Regards,
> Leonid
>
>
> 2010/7/6 Istv=E1n Zachar <z...@freemail.hu>


>
>> Dear Group,
>>
>> consider the following code:
>>

>> func[sys_Integer] :== sys;
>> func /: func[sys_Integer][args___] :== sys + 1;
>> func[sys_Function] :== sys;


>>
>> {
>> func[1],
>> func[1][2],
>> func[# + 2 &],
>> func[# + 2 &][2]
>> }
>>
>> Is there a way to define func such a way that when the main argument
>> is an Integer, any further arguments are ignored? That is, to
>> return: {1, 2, #1 + 2 &, 4} instead of {1, 1[2], #1 + 2 &,
>> 4}. At present, the second function definition is basically ignored by
>> the kernel.
>>
>> I am aware that func[sys_type, args___] would be the easiest way to
>> use here, but in my real code
>> 1. there is a large number of named options supplied with sys
>> preventing (or at least making hard) the use of *any number* of
>> optional second arguments;
>> 2. sometimes the main call (func[sys]) is separated from the
>> introduction of any optional argument (this is part of a GUI), which
>> may cause calls like this:

>> x == func[some_function]; ... (* calculations involving x *) ... ;
>> y == x[2];
>>
>> Thanks in advance,
>>
>> Istv==E1n
>>
>>
>>
>>
>>
>>

Leonid Shifrin

unread,
Jul 7, 2010, 7:43:46 AM7/7/10
to
Hi Istvan,

Hope this helps.

Regards,
Leonid


2010/7/6 Istv=E1n Zachar <z...@freemail.hu>

--001636c5a74c2f189a048ab6f60d
Content-Type: text/html; charset="ISO-8859-1"
Content-Transfer-Encoding: quoted-printable
X-Sun-Content-Length: 2579

Hi Istvan,<br><br>This was a tough one. Here is one way (it took me a while=
to figure it out, first I thought it was <br>not possible, and almost sent=
you a long explanation why it is impossible))):<br><br>ClearAll[func];<br>
func[x_Integer] :=<br> x /; ! MemberQ[Stack[_], HoldForm[func[_Integer=
][args___]]];<br>func /: func[sys_Integer][args___] := sys + 1;<br>func[s=
ys_Function] := sys;<br><br><br>In[191]:= {func[1], func[1][2], func[# =
+ 2 &amp;], func[# + 2 &amp;][2]}<br>
<br><br>Out[191]= {1, 2, #1 + 2 &amp;, 4}<br><br>I would still consider c=
hanging the design though, since this kind of difficulties / workarounds us=
ually<br>indicate to me that the design of the function in question is not =
well thought over. For example, if <br>
your sys is often supplied with additional optional arguments, you can crea=
te a wrapper say sysData (or, just <br>a List) and store them there, like f=
unc[sysData[sys,opts___]], etc.<br><br>Hope this helps.<br><br>Regards,<br>
Leonid <br>
<br><br><div class="gmail_quote">2010/7/6 Istv=E1n Zachar <span dir="lt=
r">&lt;<a href="mailto:z...@freemail.hu">z...@freemail.hu</a>&gt;</span><br=
><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; bord=
er-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
Dear Group,<br>
<br>
consider the following code:<br>
<br>
func[sys_Integer] := sys;<br>
func /: func[sys_Integer][args___] := sys + 1;<br>
func[sys_Function] := sys;<br>
<br>
{<br>
func[1],<br>
func[1][2],<br>
func[# + 2 &amp;],<br>
func[# + 2 &amp;][2]<br>
}<br>
<br>
Is there a way to define func such a way that when the main argument<br>
is an Integer, any further arguments are ignored? That is, to<br>
return: {1, 2, #1 + 2 &amp;, 4} instead of {1, 1[2], #=
1 + 2 &amp;,<br>
4}. At present, the second function definition is basically ignored by<br>
the kernel.<br>
<br>
I am aware that func[sys_type, args___] would be the easiest way to<br>
use here, but in my real code<br>
1. there is a large number of named options supplied with sys<br>
preventing (or at least making hard) the use of *any number* of<br>
optional second arguments;<br>
2. sometimes the main call (func[sys]) is separated from the<br>
introduction of any optional argument (this is part of a GUI), which<br>
may cause calls like this:<br>
x = func[some_function]; ... (* calculations involving x *) ... ;=
<br>
y = x[2];<br>
<br>
Thanks in advance,<br>
<br>
Istv=E1n<br>
<br>
<br>
<br>
<br>
<br>
<br>
</blockquote></div><br>

--001636c5a74c2f189a048ab6f60d--

István Zachar

unread,
Jul 7, 2010, 7:44:19 AM7/7/10
to
Dear Leonid,

thanks for the answer. Yes, I agree that the design is not the best in
this case, and to tell you the truth,
just after posting my question here I've decided - though I was a bit
dubious about it - to introduce the wrapper.
Now I'm much more confident with my choice as it seems to be the general
method to handle situations like this.

The only problem (at the moment) with the wrapper is that previously the
(IRL) outcome of func was a list, while
now it has the wrapper as head, making ReplaceAll calls a bit harder to
implement (like

{a, b} /. Wrapper[a -> 1, b -> 2]

which gives an errormessage. But setting UpValues for Wrapper with
ReplaceAll does help here:

Wrapper/: ReplaceAll[expr_, rep_Wrapper] :== ReplaceAll[expr, List @@ rep];

Anyway, your solution is impressive in its simplicity: I never would
have thought to fiddle with the evaluation stack,
but it's a very clever workaround! Though I won't use it, it still
remains to be a rather tricky intellectual achievement : )

Istvan


On 2010.07.06. 14:12, Leonid Shifrin wrote:

> Hi Istvan,
>
> This was a tough one. Here is one way (it took me a while to figure it
> out, first I thought it was
> not possible, and almost sent you a long explanation why it is
> impossible))):
>
> ClearAll[func];
> func[x_Integer] :==
> x /; ! MemberQ[Stack[_], HoldForm[func[_Integer][args___]]];
> func /: func[sys_Integer][args___] :== sys + 1;
> func[sys_Function] :== sys;
>
>
> In[191]:== {func[1], func[1][2], func[# + 2 &], func[# + 2 &][2]}
>
>
> Out[191]== {1, 2, #1 + 2 &, 4}
>
> I would still consider changing the design though, since this kind of
> difficulties / workarounds usually
> indicate to me that the design of the function in question is not well
> thought over. For example, if
> your sys is often supplied with additional optional arguments, you can
> create a wrapper say sysData (or, just
> a List) and store them there, like func[sysData[sys,opts___]], etc.
>
> Hope this helps.
>
> Regards,
> Leonid
>
>

> 2010/7/6 Istv=E1n Zachar <z...@freemail.hu <mailto:z...@freemail.hu>>


>
> Dear Group,
>
> consider the following code:
>
> func[sys_Integer] :== sys;
> func /: func[sys_Integer][args___] :== sys + 1;
> func[sys_Function] :== sys;
>
> {
> func[1],
> func[1][2],
> func[# + 2 &],
> func[# + 2 &][2]
> }
>
> Is there a way to define func such a way that when the main argument
> is an Integer, any further arguments are ignored? That is, to
> return: {1, 2, #1 + 2 &, 4} instead of {1, 1[2], #1 + 2 &,

> 4}. At present, the second function definition is basically ignored b=

Reply all
Reply to author
Forward
0 new messages