I think there are already some good answers about the substitute function/lazy evaluation, and how to use substitute. I just want to quote the substitute function reference (dating back to the days of the S language):
Arguments
expr: any syntactically valid R expression
env: an environment or a list object. Defaults to the current evaluation environment.
So, yes, you can use a list but that's not what does the force.
It's the way you call it as shown in this answer which shows again how to substitute on an expression in a value [1]:
substituteExpr <- function(expr, env) {
do.call(substitute, list(expr=expr[[1]], env=env))
}
substituteExpr(a^2, list(x=2)) # 100
If expr's value is an expression, substitutes in any variables (it's value, not the expression) bound in env.
Reference Details:
Substitution takes place by examining each component of the parse tree
as follows: If it is not a bound symbol in env, it is unchanged. If it
is a promise object, i.e., a formal argument to a function or
explicitly created using delayedAssign(), the expression slot of the
promise replaces the symbol. If it is an ordinary variable, its value
is substituted, unless env is .GlobalEnv in which case the symbol is
left unchanged.
Both quote and substitute are ‘special’ primitive functions which do
not evaluate their arguments.
Value The mode of the result is generally "call" but may in principle
be any type. In particular, single-variable expressions have mode
"name" and constants have the appropriate base mode.
Note substitute works on a purely lexical basis. There is no guarantee
that the resulting expression makes any sense.
Substituting and quoting often cause confusion when the argument is
expression(...). The result is a call to the expression constructor
function and needs to be evaluated with eval to give the actual
expression object.