# Using “With” outside a function on its parameters [duplicate]

This question already has an answer here:

I am making complex 3d graphics, and to clear up functions, I want to use With to remove a repeated action. This action occurs many times through many functions, so I want the With outside all of them, so I only declare it once. Except Mathematica doesn't seem to like having a With outside a function, if it is replacing parameters. A simplified version is shown below:

With[{y = x},
draw[x_] := Cylinder[{{y,0,0}, {y+1,0,1}}, 0.1]
...(*other functions:See Bottom!!!!!!!!*)
]


then:

Graphics3D[draw[3]]


If I do the above, it brings the error:

So it should go y -> x -> 3. But it only gets half-way. It has changed y to x but I think it is after when the parameters are put in, so x is not changed to 3. Is there a way to keep the with outside, so it is only declared once.

Also, in case anybody says put the With before cylinder. No, I don't want to do that, as there are other functions using the values changed by "With"

-----------------------EDIT-----------------------------

The answers can replace the values for one function, as is shown in the code example, but it doesn't show (although I did say it), that it needs to work through many functions, as shown below. Sorry for the confusion, as causing the first two answers to not fully work.

With[{y = x},
draw1[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1],
draw2[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.2]
...(*more functions*)
]

-

## marked as duplicate by Mr.Wizard♦Jul 13 '14 at 7:23

The way you have draw set up, x is defined within draw as a parameter. Outside of draw, x is just a symbol x, so for example draw[3] will yield the output Cylinder[{{x, 0, 0}, {x, 0, 0}}, 0.1]. In other words, Mathematica doesn't think the formula you've given it for draw involves the input variable at all. –  Alexander Gruber Jul 12 '14 at 16:32
closely related, maybe duplicate: 48521. So you can proceed with Function[ draw[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 0}}, 0.1] ][x], notice I've added +1 since your code makes no sense. –  Kuba Jul 12 '14 at 16:48
related tutorial –  Kuba Jul 12 '14 at 17:03
@Kuba,@Jacob Akkerboom. Yes you are both right, my code it incorrect, in both the ways you two stated, I have now changed it. –  ptolemy0 Jul 12 '14 at 17:10
Responding to your edit: Function[ draw1[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 1}}, 0.1]; draw2[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 1}}, 0.2];][x] –  Kuba Jul 12 '14 at 17:44

There is a mismatch in your definition, between x and x$ draw // Information  Globaldraw draw[x$_]:=Cylinder[{{x,0,0},{x,0,0}},0.1]...


This is because With is a so called scoping construct. With tries to avoid conflicts between symbols. See this answer for a good explanation by Leonid. If you want to know all cases in which this happens, see this question by me.

If you wish to avoid such behaviour, you can use ReplaceAll on the outside, rather than With. There is another issue with your code too, as the two points in the first argument of Cilinder have to be different. So I have changed this. You could then use the following code

Unevaluated[draw2[x_] := Cylinder[{{y,0,0}, {y,0,1}}, 0.1]]/. Unevaluated[y->x]


Because the syntax with Unevaluated is a bit ugly, you may consider making your own function, like this.

SetAttributes[replaceWith, HoldFirst]
(*Unevaluated[y] is to make sure replaceWith works well when providing
Unevaluated around its second argument*)
replaceWith[x_,y_]:= Unevaluated[x]/.Unevaluated[y]


The solution is then

replaceWith[
draw2[x_] := Cylinder[{{y, 0, 0}, {y, 0, 1}}, 0.1]
,
Unevaluated[y -> x]
]


In both cases, we have

draw2 // Information


Globaldraw2

 draw2[x_]:=Cylinder[{{x,0,0},{x,0,1}},0.1]


Edit

You asked to make it work for multiple definitions. The solution is simply as follows

replaceWith[
draw1[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1];
draw2[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.2]
,
y -> x
]


Note that I have replaced a colon of yours by a semicolon. I prefer it this way, as this corresponds more closely to With (note that syntax in the example you provided was not correct). We then have

Graphics3D[{draw1[1], draw2[1]}]


-

When you do this, localization kicks in and renames one of the x to avoid conflict:

This is in general beneficial, and designed to prevent trouble. In some situations, however, you don't want this localization-through-renaming to happen. In that case you can use Replace instead of With to replace y by x.

I like to keep using the syntax of With though, so in these situations I tend to use the withRules function defined in the middle of this answer.

The caveat with withRules is precisely that it does not truly localize, so you need to be very careful when using it.

Update:

Version 10-specific template based solution:

template = (draw[x_] := Cylinder[{{#y, 0, 0}, {#y + 1, 0, 1}}, 0.1]) &

template[<| "y" :> x |>]

?draw

draw[x_]:=Cylinder[{{x,0,0},{x+1,0,1}},0.1]

-
I see our answers are quite similar :). I must say I don't like a definition using InternalInheritedBlock on Rule and RuleDelayed though. That is not necessary in my opinion. Moreover, the Front End also us Rule and RuleDelayed and modifying their behaviour may interfere with dynamic behaviour. But I suppose my own function isn't perfect either :). –  Jacob Akkerboom Jul 12 '14 at 17:25
@Jacob The modification is temporary (localized), so it shouldn't cause any problems. I guess you are referring to the situation when the main evaluation is interrupted by a pre-emptive one (dynamic) and the modification is still in effect. Yes, that can be a problem with any similar use of Block as well ... I think that's a general problem with the language. The way you defined replaceWith, it's not going to allow evaluation on the RHS of -> (not :>) while also being self-contained. –  Szabolcs Jul 12 '14 at 17:47

You could also do this :

With[{y = x},
SetDelayed @@ {Unevaluated[draw[x_]],
Unevaluated[Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1]]}
];
`
-