I am trying to use Thread to apply a function involving RandomReal but it does not work as I expected.

If I have the function

f[x_, y_] := x + y


I can use Thread to map over a list of x values of interest

Thread[f[{1, 2, 3}, 4]]


I cannot, however, do this if I have the function

f[x_, y_] := RandomReal[{x , y}]


If I call the Thread function again I get the following error

I expected to get as output the results of

{f[1,4], f[2,4], f[3,4]}


so basically 3 random numbers. Probably more importantly than why this doesn't work - is how can I replicate this behaviour when using RandomReal in the function?

• Side note: f[#, 4]&/@{1,2,3} is more natural in this case. – xzczd Jun 30 '20 at 3:57
• @xzczd I didn't see your comment but we're thinking the same. The mechanism of Thread is somewhat a bit of complicated and I rarely use it. – SneezeFor16Min Jun 30 '20 at 4:44

Thread[Unevaluated@f[{1, 2, 3}, 4]]

{1.946182809, 2.842898138, 3.571798651}


## Explanation

Thread evaluates the whole expression before threading.

f[x_, y_] := x + y

=> Thread[{1, 2, 3} + 4] (* Evaluates *)
=> {5, 6, 7} (* Threads... trivially *)


Since + is Listable, nothing went wrong even though f was evaluated first.

f[x_, y_] := RandomReal[{x, y}]

=> Thread[RandomReal[{1, 2, 3}, 4]] (* Evaluates... *)
=> (* Error *)


"Evaluate first" passes wrong arguments to RandomReal.

f[x_, y_] := RandomReal[{x, y}]

=> Thread[f[{1, 2, 3}, 4]] (* Evaluates *)
=> ...


Unevaluated helps f survive the evaluation.

f[x_, y_] := RandomReal[{x, y}]
list = {1, 2, 3};

=> Thread[f[list, 4]] (* Evaluates *)
=> RandomReal[{1, 2, 3}, 4]
=> (* Error *)


list should not survive the evaluation, while f should. We can use Inactive on f and Activate afterwards:

f[x_, y_] := RandomReal[{x, y}]
list = {1, 2, 3};

=> Activate@Thread[Inactive[f][{1, 2, 3}, 4]] (* Evaluates *)
=> Activate@{Inactive[f][1,4], Inactive[f][2,4], Inactive[f][3,4]} (* Threads *)
=> {f[1,4], f[2,4], f[3,4]} (* Activates *)
=> ...


Actually, there're many simpler and faster ways to do this, like:

f[#, 4] & /@ list

• Hope I can get away with a follow-up question. Why does this fail if you replace the {1,2,3} with something like list = {1,2,3}; Thread[Unevaluated@f[list, 4]] ? Is it because of the Unevaluated? – Esme_ Jun 29 '20 at 22:57
• @Esme_ Yes, in this case the {1,2,3} is not explicit. In other words, Thread cannot see the {1,2,3} inside list. A possible solution: list = {1, 2, 3}; With[{list = list}, Thread[Unevaluated@f[list, 4]]], another: list = {1, 2, 3}; Thread[Unevaluated@f[#, 4]] &@list. – xzczd Jun 30 '20 at 3:48
• @Esme_ Yes, please see my updated answer. – SneezeFor16Min Jun 30 '20 at 4:04
• Thanks SneezeFor16Min and xzczd for the explanations and detailed answers. Despite many years of using Mathematica I'm still only touching the surface! – Esme_ Jun 30 '20 at 5:47