23

Here's a simplified version of what I'm trying to do:

SetAttributes[def, HoldFirst]
def[s_Symbol, v_] := (s[x_] := v)
def[f, x^2]
f[3] (* Expected result: 9 *)
(*
  x^2
*)
?f (* Expected result: f[x_] := x^2 *)
(*
  Global`f

  f[x$_] := x^2
*)

Obviously the x in the x_ pattern gets replaced by x$. Is there a way I can prevent that? That is, from calling def[f,x^2] I want to result the definition f[x_] := x^2. I don't of course care about the name of the variable, so if the resulting function definition reads f[x$_] := x$^2 I'm fine with that, too.

I tried

def[s_Symbol, v_] := With[{x$ = x}, s[x_] := v]
def[s_Symbol, v_] := With[{x = x$}, s[x_] := v]
def[s_Symbol, v_] := (s[x_] := v) /. x :> x$

and

def[s_Symbol, v_] := (s[x_] := v) /. x$ :> x

but none worked.

Kuba
  • 136,707
  • 13
  • 279
  • 740
celtschk
  • 19,133
  • 1
  • 51
  • 106

3 Answers3

20

Try for example

SetAttributes[def, HoldAll]
def[s_Symbol, v_] := Function[Null, s[x_] := #, HoldFirst][v]

Unnamed functions just don't care :)

Other alternatives that should also work (but I would use the previous approach)

def[s_Symbol, v_] := Identity[SetDelayed][HoldPattern@s[x_], v];
def[s_Symbol, v_] := Unevaluated[s[x_] := "Hello"] /. "Hello" -> v
Oleksandr R.
  • 23,023
  • 4
  • 87
  • 125
Rojo
  • 42,601
  • 7
  • 96
  • 188
  • Elegant! +1. (I edited to better respect the semantics of SetDelayed. If you don't like the result, feel free to revert!) – Oleksandr R. Aug 31 '12 at 21:33
  • Thanks @OleksandrR. Your edit is fine. I had done it that way based on the question being HoldFirst, but HoldAll makes more sense to me too – Rojo Sep 01 '12 at 01:21
20

With your proposed definition style, the user of that function def has to know that v could/should/must depend on x for this to work; x really should be an argument of def. Perhaps something like this were better suited.

ClearAll[def]
ClearAll[f]
(*SetAttributes[def,HoldFirst]*)

def[s_Symbol, v_, vars_List] := 
 With[{h = s @@ (Pattern[#, Blank[]] & /@ vars)}, (h := v)]
def[f, x^2, {x}]

f[3]
(* 9 *)
11

I propose these:

SetAttributes[def, HoldAllComplete]

def[s_Symbol, v_] := SetDelayed @@ Hold[s[x_], v]

def[s_Symbol, v_] := With[{L := s[x_]}, L := v]

def[s_Symbol, v_] := Reverse @ Unevaluated[v := s[x_]]

The HoldAllComplete (or SequenceHold) attribute is necessary for an assignment such as:

def[q, Sequence[1, 2, x]]

head[q[5]]
head[1, 2, 5]

Also see:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371