4
$\begingroup$

I'm trying to use Modules together with functions.

tmp2 = x^2 + 1; f[y_] := Module[{x = 1}, Evaluate[y tmp2]]

This works when I just have

tmp2 = x^2 + 1; Module[{x = 1}, Evaluate[tmp2]]

But f[1] outputs $1 + x^2$.

Edit: After seeing the answers I wonder how I can get something like this to work:

ClearAll[withRules]
SetAttributes[withRules, HoldAll]
withRules[rules_, expr_] := 
 Internal`InheritedBlock[{Rule, RuleDelayed}, 
  SetAttributes[{Rule, RuleDelayed}, HoldFirst];
  Unevaluated[expr] /. rules]
ClearAll[f, tmp2]
tmp2[y_] = y (x^2 + 1)
withRules[{expr -> tmp2}, f[y_] := Module[{x = 1}, expr[y]]]

Edit 2: Maybe it is better if I explain what I'd like to do.

I have a lot of calculations in terms of $k_x$, $k_y$ and $k_z$. So, those are outputted in terms of these three variables. However, sometimes I need to integrate such expressions but to do this I need to use spherical coordinates. So I set $k_x = k \sin(a) \cos(b)$ and so on and then integrate. I don't want the transformations always set because I want the other calculations to have $k_x$, $k_y$ and $k_z$ as variables, not $a$ en $b$.

$\endgroup$
5
  • $\begingroup$ Please post a minimal example. We don't know what Trew does (nor does it seem to be relevant to the question), we don't know what result you're expecting or not expecting, the code is not indented properly and therefore hard to read. $\endgroup$
    – David
    Feb 19, 2012 at 21:07
  • $\begingroup$ @David: Good points. I think I have reduced the problem to what my problem is... $\endgroup$
    – JT_NL
    Feb 19, 2012 at 21:12
  • $\begingroup$ It works fine for me: imgur.com/3SatR - maybe you didn't clear some variables and have unwanted side effects? Can you reproduce the error with your example? $\endgroup$
    – David
    Feb 19, 2012 at 21:18
  • $\begingroup$ @David: After restarting the kernel I get this: i.imgur.com/fFNw8.png. And why does it output $5$ for you? $1^2 + 1 = 2$ I'd say... $\endgroup$
    – JT_NL
    Feb 19, 2012 at 21:23
  • $\begingroup$ Please see my edit for one possibility $\endgroup$ Feb 19, 2012 at 22:24

2 Answers 2

6
$\begingroup$

Ok, let me tell you straight away that what you are trying to do is likely to be a bad idea. Now, let me explain. The problem you face on the surface is that Evaluate is only effective at the first level inside heads which hold their arguments. Since you have SetDelayed[f[y_],Module[...]], the stuff inside Module is too deep. You need With to inject arbitrarily deep, so we try:

ClearAll[f, x];
With[{tmp = tmp2},
   f[y_] := Module[{x = 1}, y tmp]] 

However, now you face another problem: variable renaming mechanism in scoping constructs tries to protect the scoping and renames variables in part of your code:

?f
Global`f
f[y$_]:=Module[{x$=1},y$ (1+x^2)]

Here is what you can do to achieve your goal:

ClearAll[f, x];
Block[{tmp}, Unevaluated[f[y_] := Module[{x = 1}, y*tmp]] /. tmp -> tmp2]

This is explained in details here. Note however that the fact that we had to go against the system twice tells us already that this is a fragile and generally bad practice. Please see more discussion on that in the linked answer.

EDIT

In response to the edit in the question: in your case, I suggest to use Block and create a dynamic environment, where to execute the code for which you do want your transformations:

SetAttributes[withTransforms,HoldAll];
withTransforms[code_]:=
 Block[{kx=k*Sin[a]*Cos[b],ky=k*Sin[a]*Sin[b],kz=k*Cos[b]},
   code
 ]

This will allow you to selectively enable these definitions:

withTransforms[Integrate[kx^2+ky^2,{a,0,2 Pi}]]
$\endgroup$
3
  • $\begingroup$ I feel this is my fault, please see where this question may have come from: this comment and the preceding one. I was trying to squeeze into the comment a way to define that f function (to compute the eigenvalue) in terms of the already existing matrix. Since you are saying that this is bad practice, I felt it necessary to link to that discussion. I agree that this is a messy thing and should be avoided when possible, but occasionally it's useful. $\endgroup$
    – Szabolcs
    Feb 19, 2012 at 21:52
  • $\begingroup$ @Szabolcs You don't sound logical now. Either you agree with my point that this is not a good practice, but then your answer (which, in fact, is not very different from mine except this point) does not reflect that, or you don't, but then why saying the question is your fault? $\endgroup$ Feb 19, 2012 at 21:56
  • $\begingroup$ @Szabolcs As to the occasional usefullness, I agree generally but strongly disagree for this particular class of use cases, because I think that taking the scope apart in this way is very fragile. I do sometimes use definition-time replacements, like e.g. in the lex function from this answer, but I try to avoid breaking scope, particularly nested scope, and particularly with global objects. This was also endlessly discussed on Mathgroup and lead to lots of trouble in all cases I remember. $\endgroup$ Feb 19, 2012 at 22:17
3
$\begingroup$

What you are trying to do is generate code (a function definition) programmatically.

There are several techniques to programmatically generate code. The main tool we need is injecting held expressions into other held expression. The usual way to do it is using With:

tmp2 = x^2 + 1;

With[{expr = tmp2},
 f[y_] := Module[{x = 1}, y expr]
]

Now let's check the function definition:

?f

Global`f
f[y$_]:=Module[{x$=1},y$ (1+x^2)]

You can see that Mathematica's localization mechanism has renamed x to x$ in Module, so this didn't work.

In these situations we can usually use some solution based on Replace or ReplaceAll.

It's advantageous to have this packaged up into a function. I tend to use the withRules function that I described here:

ClearAll[withRules]
SetAttributes[withRules, HoldAll]
withRules[rules_, expr_] :=
  First@PreemptProtect@Internal`InheritedBlock[
    {Rule, RuleDelayed},
    SetAttributes[{Rule, RuleDelayed}, HoldFirst];
    Hold[expr] /. rules
]

It is used in a similar way to With, but is does not localize variables like With does.

Clear[f]
withRules[{expr -> tmp2},
 f[y_] := Module[{x = 1}, y expr]
]

Checking the definition of f again we can see that now it is what we intended:

?f

Global`f
f[y_]:=Module[{x=1},y (1+x^2)]

I have asked a related question recently, and in that post you'll find a slightly more general expression injection function (inject[]), as well as several other expression injection techniques described in the answers:

$\endgroup$
3
  • $\begingroup$ @Mr.Wizard I don't think I should delete this time. I agree with @Leonid that often it is not good practice to do this (especially since tmp2 is global), but there are legitimate situations, and I was in fact trying to do this an hour ago in this comment $\endgroup$
    – Szabolcs
    Feb 19, 2012 at 21:34
  • $\begingroup$ @JonasTeuwen Why won't you just use Integrate[expr /. kx -> k Sin[a] Cos[b], {a, ...}, {b, ...}] then? You should almost never need to use code generation for this kind of thing. As we say, it's like shooting at a sparrow with a cannon (and it's a cannon that can fire back this time---I have to agree with Leonid on that) $\endgroup$
    – Szabolcs
    Feb 19, 2012 at 22:20
  • $\begingroup$ Thanks. (I had deleted my comment as I thought it would be better to just put this in my original post). I came to this "idea" while searching for "local variables mathematica". $\endgroup$
    – JT_NL
    Feb 19, 2012 at 22:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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