Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Consider this example: given an option for a function func as "x" :> (a&), how can one assign different values to a locally inside func?

a = False; (* global value of a *)
func[opt_] := With[{a = True}, Evaluate["x" /. opt]]; (* oversimplified function *)

func["x" :> (a &)]    (* ==> a & *)

No amount of Evaluate seems to help here. The problem with With is that in this case it is interpreted as With[{a$ = 1}, a&], thus no replacement will be done. If I use Block instead of With the problem boils down to this (as Block won't replace variables inside Function):

 Block[{a = True}, a &]   (* ==> a & *)

I do not want to define the option to be a function depending explicitly on a: I expect the user to write his own pure function for "x" where he can also use a that should acquire its local value when the With/Block is evaluated. Also, the problem disappears, if the code is simplified, but of course this is a no-go, as I need a function:

opt = "x" :> (a &);
With[{a = True}, Evaluate["x" /. opt]]   (* ==> True & *)

I've checked the following posts (mostly by Leonid and Mr.W), none of which offered a clear solution: this, this and this.

share|improve this question
1  
How about something like f[opt_] := "x" /. (opt /. HoldPattern[a] -> True)? –  ssch Oct 19 '13 at 11:36
    
A variation on @ssch: func[opt_] := Block[{a = True}, "x" /. opt /. HoldPattern[a] -> a] –  Michael E2 Oct 19 '13 at 12:09
    
@MichaelE2 Well, your version is a special case of what my code does in general. For the record, I haven't seen the comment of ssch before posting my code. –  Leonid Shifrin Oct 19 '13 at 12:14
    
@LeonidShifrin Yes, I see that now. I was working on it myself and worked my way around to ssch's. But I thought pointing out the use of Block worth a comment, since Istvan mentioned a taking on local values. I saw you had answered, and saw it was general, but it took a while for me to figure out what it was doing. I wish I was as fluent as you (as you seem to be) in thinking about these problems, which is why I try to solve them. (Upvoted your answer, btw.) –  Michael E2 Oct 19 '13 at 12:33
    
@MichaelE2 Thanks :) I realize that some of my solutions can be cryptic, and it would probably have taken me quite some time to decipher such code, should I run across one written by someone else. But from the pure language viewpoint, I think that this type of code is rather economical. It just needs some time to get used to this style. –  Leonid Shifrin Oct 19 '13 at 12:36

4 Answers 4

up vote 12 down vote accepted

To avoid those scoping constructs being recognized as such and having their variables renamed, I like wrapping their heads with Identity. In your case,

func[opt_] := Identity[With][{a = True}, "x" /. opt]
share|improve this answer

This question is a subset of: Enforcing correct variable bindings and avoiding renamings for conflicting variables in nested scoping constructs and related to: Nested definition: How can I define a function with a passed-in expression?

In this case, since you explicitly evaluated the body of With anyway, I would simply use Function as ybeltukov did, but without the Options method (though I like his way too):

func[opt_] := With[{a = True}, #] &["x" /. opt]

In a more general case, if you needed to hold the body of With unevaluated I would Apply With:

func[opt_] := With @@ Hold[{a = True}, "x" /. opt]

I find this a bit more intelligible than using Identity[With] as Rojo did. If intelligibility is not a priority here are some other methods for the sake of curiosity:

func[opt_] := With[Unevaluated[{a = True}], "x" /. opt]

func[opt_] := With[{L := {a = True}}, With[L, "x" /. opt]]
share|improve this answer
    
+1. Nice, I like the application of With –  RunnyKine Oct 20 '13 at 3:05

Solution with Options:

Options[func] = {"x" -> None};  (* some default value *)

func[OptionsPattern[]] := With[{a = True}, #] & @@ 
          OptionValue[Automatic, Automatic, "x", Hold];

func["x" :> (a &)]

True &

share|improve this answer

A quick solution by ssch

For the case at hand, you can do

f[opt_] := "x" /. (opt /. HoldPattern[a] -> True)

A general solution to this class of problems

Here is a general way to perform this type of tasks, also based on replacement rules.

It looks like you are after some function which would rebind certain bindings captured in e.g. closure (bindings to the enclosing environment). Here is a possibility:

ClearAll[rebind];
SetAttributes[rebind, HoldAll];
rebind[vars : {__Set}, code_] :=
  Block[vars,
    code /. Flatten[
        Cases[
          Hold[vars], 
          HoldPattern[sym_Symbol = _] :> OwnValues[sym], 
          {2}
        ]]]

This will create dynamic bindings for a given set of variables, using Block, and then inject them lexically into the body of the code, using replacement rules. The use of replacement rules here is preferable to the use of With, because the former don't care about the variable collisions in inner scoping constructs - which seems to be what we need here.

Here is your example:

func[opt_] := rebind[{a = True}, "x" /. opt];

and

func[opt]

(* True & *)

Note that this is still a rather exotic construct, so I would make sure that there is a real need to use things like this.

share|improve this answer
    
@ssch, Leonid Somehow I thought that this could be solved with a scoping construct and did not thought of direct replacements. Silly me, thanks for the answer. I think it would be useful to edit in ssch's shorter form as well as a quick fix! –  István Zachar Oct 19 '13 at 12:18
    
@IstvánZachar All right, edited that in. –  Leonid Shifrin Oct 19 '13 at 12:25
    
That Cases and OwnValues combination is really neat! –  ssch Oct 19 '13 at 12:47
    
@ssch Thanks, I like this bit too. –  Leonid Shifrin Oct 19 '13 at 14:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.