How can I hold UpValues but evaluate other expressions?

11

6

Consider these definitions:

own = "OwnValue";
down[_] = "DownValue";
sub[_][_] = "SubValue";
N[n] = 3.14;
_[___, up, ___] ^= "UpValue";


The attribute HoldAllComplete holds an UpValue but it also holds the other Values as well.

Without advance knowledge of the symbol up how can I evaluate everything but the UpValue?

Set and related functions appear to have this evaluation property internally:

f[own, down[1], sub[1][2], N[n], up] = 1;

Definition[f]

f["OwnValue", "DownValue", "SubValue", 3.14, up] = 1


The first idea that comes to mind is to test if a symbol has an UpValue and skip evaluation if it does, but this proves problematic. First, a symbol can have both an OwnValue and an UpValue, and the OwnValue should be used if possible:

x[up3] ^= 2;
up3 = 1;

f[up3]

f[1] (* desired output *)


Second, testing for an UpValue can be difficult:

_[___, up4, ___] ^= {};

UpValues[up4] === UpValues[Plus]


True

To clarify, it is not my intent to return f[. . ., up] as output, which would require Defer or similar. Rather I would like to handle the expression f[. . ., up] as an argument like Set does, or define a function f[args___] := . . . (with attribute HoldAllComplete) that returns e.g. {"OwnValue", "DownValue", "SubValue", 3.14, HoldComplete[up]}

How can this be achieved?

When you write f[up]=1, up appears at the second level. This is the reason why the UpValue does not evaluate. Consider x=up (which is Set[x,up])---this evaluates to "UpValue" right away. Now try SetAttributes[fun, HoldAll] and fun[p[up]]---this one does not evaluate because UpValues are evaluated only in the first level of held heads (just like Unevaluated, Sequence, etc.) – Szabolcs – 2012-07-21T10:03:28.400

@Szabolcs believe it or not I know this. :^) I guess I didn't formulate my question all that well. Would you join me in Chat to work it out? – Mr.Wizard – 2012-07-21T10:05:31.883

9

Does this work as you want to?

SetAttributes[f, HoldAllComplete];
{first, rest___} ^:= HoldComplete[rest]
f[args___] := {first, args}

f[own, down[1], sub[1][2], N[n], up]


HoldComplete["OwnValue", "DownValue", "SubValue", 3.14, up]

Competing UpValues! Brilliant. – Mr.Wizard – 2012-07-21T22:36:04.150

6

If I understand it correctly, the gist of your question is going from

HoldComplete[x, y, z, up]


to

HoldComplete[1, y^2, z, up]


assuming the following definitions:

_[___, up, ___] ^= "UpValueEvaluated"
x = 1
f[x_] := x^2


That is, evaluate everything inside the HoldComplete except the UpValue. I managed to do this using the following construction:

InternalInheritedBlock[
{RuleCondition},
Attributes[RuleCondition] = {HoldAllComplete};
Replace[HoldComplete[x, f[y], z, up], e_ :> RuleCondition[e], {1}]
]


The undocumented RuleCondition is explained here. This construction should be able to emulate the behaviour of Set.

1Intersting one... – Rojo – 2012-07-21T12:08:52.320

@Rojo I'm still reading the other solutions. – Szabolcs – 2012-07-21T12:11:30.813

2+1, clever. If desired, we can avoid using InternalInheritedBlock by means of a helper identity function: SetAttributes[hci, HoldAllComplete]; hci[x_] := x. Then we can write: Replace[HoldComplete[x, f[y], z, up], e_ :> RuleCondition @ hci @ e, {1}]. – WReach – 2012-07-21T17:57:00.873

@WReach & Szabolcs I don't understand how this works (both methods). Please enlighten me! – Mr.Wizard – 2012-07-21T22:39:57.373

@Mr.Wizard Here's the key: http://stackoverflow.com/questions/6633236/replace-inside-held-expression/7679152#7679152 It's a way to evaluate any subexpression inside a held expression. I take all elements of the HoldComplete and evaluate them one-by-one. I need to take care on the way not to end up with something[up] where something doesn't have HoldAllComplete (this way the challenge).

– Szabolcs – 2012-07-21T22:43:47.810

@Mr.Wizard HoldAllComplete suppresses the application of up-values. That is one of main differences between it and HoldAll. Combined with the held-evaluation trick, the desired behaviour is obtained -- at least for the sample cases. Nested sequences and other less-frequent constructs may pose further problems, but it is a clever solution just the same. – WReach – 2012-07-21T22:55:37.167

I'm aware of RuleCondition though I don't claim to fully understand its internals. I still however do not understand the mechanism by which this evaluates other things besides the UpValue. – Mr.Wizard – 2012-07-21T22:56:21.597

5

To answer my own question and further illustrate the kind of operation I am describing, here is a method using Set itself:

SetAttributes[f, HoldAllComplete]

f[args___] :=
Module[{h},
h[args] = 1;
Level[DownValues@h, {4}, HoldComplete]
]

f[own, down[1], sub[1][2], N[n], up]

HoldComplete["OwnValue", "DownValue", "SubValue", 3.14, up]