That's an interesting first question. Welcome. :-)
From a simplistic perspective this should work, but as you observe there are evaluation properties that are more complex. Here is a reference for most (but not all) behavior:
The Standard Evaluation Sequence
Let's follow those steps for your example.
Heads are evaluated first
Evaluate the head h of the expression.
The first actual step of evaluation is to evaluate the head of any expression. Therefore:
list[[2]][ (* anything *) ]
Evaluates to:
Plot[ (* anything *) ]
Hold attributes
Evaluate each element of the expression in turn. If h is a symbol with attributes HoldFirst, HoldRest, HoldAll, or HoldAllComplete, then skip evaluation of certain elements.
See these documentation pages for reference: HoldAll
, HoldFirst
, HoldRest
, SequenceHold
, HoldAllComplete
.
These Attributes
control the evaluation of arguments within a function, as stated above. Let's check the attributes of Plot
:
Attributes[Plot]
{HoldAll, Protected}
We see that it has HoldAll
. Because of this its arguments, in the case of your example list[[3]]
and list[[1]]
are not evaluated, and because these expressions are not valid input (do not match the pattern in the function definition) for Plot
evaluation stops and an error message is issued:
Plot::pllim: Range specification list[[1]] is not of the form {x, xmin, xmax}. >>
An apparent solution for the simple case
Now we can follow the method by which your working code succeeds. If we build an expression with head f
, which does not have a Hold attribute, the arguments evaluate:
f[list[[3]], list[[1]]]
f[Sin[x], {x, 1, 10}]
We could also use List
in place of f
, which is the most frequent choice for this particular operation and which I will use hereafter. At this point it is possible to replace f
or List
with another head, done most easily with Apply
:
list[[2]] @@ { list[[3]] , list[[1]] }
Which appears to work. But does it?
Let us now set a value for x
beforehand:
x = 0;
This does not disrupt Plot
:
Plot[Sin[x], {x, 1, 10}] (* produces graphic *)
But see what happens when we try our reconstructed expression now:
list[[2]] @@ { list[[3]] , list[[1]] }
During evaluation of In[142]:= Plot::itraw: Raw object 0 cannot be used as an iterator. >>
Plot[0, {0, 1, 10}]
Clearly not what we intended. You see that Plot
has HoldAll
for a reason: to prevent the premature evaluation of its arguments.
A more robust solution
You may also see that defining the parts of an expression to reconstruct in a bare list is probably not a good idea, so let's do something about that now:
exprs = Hold[{x, 1, 10}, Plot, Sin[x]]
Now despite the global value assigned to x
we can manipulate these expressions:
exprs[[{3}]]
Hold[Sin[x]]
Note that because of the brackets { }
and for the reason described here the expression Sin[x]
is returned wrapped in Hold
, and x
does not evaluate. (If we used exprs[[3]]
we would get the raw expression and evaluation would continue.)
We can now attempt to reconstruct our Plot
expression:
exprs[[{2}]][exprs[[{3}]], exprs[[{1}]]]
Hold[Plot][Hold[Sin[x]], Hold[{x, 1, 10}]]
We see that the Part
calls have evaluated as desired and that the expressions themselves have not evaluated, also as we desired. At this time we can use the function ReleaseHold
which will strip one level of Hold
from each expression, yielding our final result:
% // ReleaseHold
Other considerations
So far I have discussed only the first two steps (after the non-evaluation rule for raw objects) of the Standard Evaluation Sequence. They happen to be the most relevant, but they are not complete.
UpValues
Within the standard evaluation sequence we have at least one more issue to address: UpValues. These are definitions created using UpSet
, UpSetDelayed
, TagSet
, or TagSetDelayed
. The definitions (rules) are attached to Symbols that when used inside another function (at the first level) cause execution (matching and replacement) of the rule before the definitions for the head of the main expression. As an example we can make a very general (and dangerous) definition that will replace (nearly) any expression which has as one of its arguments up[]
with "UpValue triggered!"
:
up /: _[___, up[], ___] := "UpValue triggered!"
We can see that Hold
does not contain this:
Hold[1, 2, up[], 3] (* outputs: "UpValue triggered!" *)
This is the purpose of HoldComplete
and the attribute HoldAllComplete
:
HoldComplete[1, 2, up[], 3] (* does not evaluate *)
We should therefore use these functions when attempting to extend this reconstruction method to arbitrary expressions.
Other methods for building an expression
Leaving aside whatever manipulations you wish to do with this "list" expression parts it may be more convenient assemble them using something other than Part
or Extract
. Two methods are: Function
, and replacement patterns (see "injector pattern").
The replacement pattern has the advantage of being concise and, to me, readable. Specifically one does not need to juggle additional intermediate functions and attributes as the replacement is done structurally, before evaluation continues.
exprs /. Hold[arg2_, head_, arg1_] :> head[arg1, arg2] (* produces graphic *)
The other method is more verbose, using the third parameter of Function
to set an attribute so that the arguments of the created function (the expression pieces) do not pre-evaluate:
Function[{arg2, head, arg1}, head[arg1, arg2], HoldAllComplete] @@ exprs
Or using an undocumented syntax (Null
as first argument; ignore the syntax highlighting):
Function[Null, #2[#3, #1], HoldAllComplete] @@ exprs