11

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?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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 Jul 21 '12 at 10:03
  • @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 Jul 21 '12 at 10:05

3 Answers3

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]

Rojo
  • 42,601
  • 7
  • 96
  • 188
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:

Internal`InheritedBlock[
 {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.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • 1
    Intersting one... – Rojo Jul 21 '12 at 12:08
  • @Rojo I'm still reading the other solutions. – Szabolcs Jul 21 '12 at 12:11
  • 2
    +1, clever. If desired, we can avoid using Internal`InheritedBlock 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 Jul 21 '12 at 17:57
  • @WReach & Szabolcs I don't understand how this works (both methods). Please enlighten me! – Mr.Wizard Jul 21 '12 at 22:39
  • @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 Jul 21 '12 at 22:43
  • @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 Jul 21 '12 at 22:55
  • 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 Jul 21 '12 at 22:56
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]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371