The reason why With
works and Block
does not has to do with the sequence of evaluation and the HoldAll
attribute of Plot
. Because of the HoldAll
attribute, Plot
gets to decide when it wants to evaluate any of its arguments. This can be confusing, but the confusion is usually around the first argument, the "body" of the Plot
. People will create an expression, save it as a variable, pass that variable to Plot
, and get unexpected results or errors. This is because Plot
only sees that variable until it decides to "unpack" it, and it's at that moment that things start being evaluated. In your case, the confusion is coming with the options.
Let's look one of your examples:
pWrapG[opt : OptionsPattern[Plot]] :=
Block[{pArgs},
pArgs = Sequence @@ FilterRules[{opt}, Options[Plot]];
Plot[x^2, {x, 1, 5}, pArgs]]
Sure, pArgs
is now a local variable and is assigned a value, but the expression
Plot[x^2, {x, 1, 5}, pArgs]
still "sees" aArgs
, the literal symbol, because Plot
has the HoldAll
attribute. I don't know the implementation details of Plot
, but apparently it expects options to be a list of rules that it can inspect directly without needing to evaluate anything first. One fix would be to interrupt the normal sequence of evaluation and force the symbol to be evaluated before Plot
"gets its hands on it":
Plot[x^2, {x, 1, 5}, Evaluate[pArgs]]
Okay, so why did it work with With
? Because With
also has the HoldAll
attribute, and is designed to work in a particular way. It evaluates all of the local variables in its first argument and does the specified replacements on the main body before anything in the body evaluates. The With
effectively disappears, and it's as if those local variables never existed. So in my example
pWrapG[opts : OptionsPattern[]] :=
With[
{filteredOpts = Sequence @@ FilterRules[{opts}, Options[Plot]]},
Plot[x^2, {x, 1, 5}, filteredOpts]]
Plot
never sees filteredOpts
, it only ever sees the expression assigned to filteredOpts
, which is a sequence of rules as desired.
Now, having said all of that, ListPlot
does not have the HoldAll
attribute. So you don't need to play these games with ListPlot
. For your pWrapX
example, we could just do this:
pWrapX[(assoc_)?AssociationQ, opt : OptionsPattern[ListPlot]] :=
With[
{xyPairs = Lookup[assoc, "Data", {}],
subjectIdForLabel =
"Subject: " <> Lookup[assoc, "SubjectID", "default ID"]},
With[
{plotRange = (1.1 Max@Abs@Flatten@xyPairs) {{-1, 1}, {-1, 1}}},
ListPlot[
xyPairs,
opt, ImageSize -> Large, AspectRatio -> 1,
PlotTheme -> "Detailed", PlotLabel -> subjectIdForLabel,
PlotRange -> plotRange]]]
Usage:
data = <|"Data" -> ({#, 5 Cos[(2 Pi) #/2 + Pi]} & /@ Range[-5, 5, .001]), "SubjectID" -> "LA01", "Period" -> 2|>;
pWrapX[data]
And any override options you provide will take precedence just because of the way options are handled.