I've now read the official documentation of wolfram on (anonymous) pure functions, and the part about (anonymous) pure functions in the book "mathematica programming intro".

I believe I understand basically how (anonymous) pure functions work.

However, after looking at the examples, it seems to me that everything that can be done with anonymous pure functions, could also be done with "standard functions".

So why would one bother to use the (in my opinion visually less clear) construct of pure functions?

(Note, I'm not talking about the more abstract notion of a pure function, meaning a function that "doesn't have side effects". I understand why one would want to build up a program based on those. I'm talking about the specific worlfram language construct.

  • 1
    $\begingroup$ if you're worried about readability, Function might be helpful, as opposed to the hard-to-read #& notation. it's basically just a matter of style, though. python is a language that has support for lambdas but often people are encouraged to declare normal functions instead $\endgroup$ Jan 26 '18 at 18:38
  • $\begingroup$ closely related: functions-vs-patterns related: 163992 $\endgroup$
    – Kuba
    Jan 26 '18 at 19:18
  • $\begingroup$ An important use case for pure functions is when you want to pass a function that you create at run-time, to some other function, as a parameter, and not think too hard about resource management and garbage collection (so that the inner state of that passed function gets GC-d when the function is no longer used). I have posted a more detailed explanation of this here. In particular, this becomes important when one uses the programming style that relies on closures. $\endgroup$ Jan 28 '18 at 13:23

Think of it as a macro.

  • It shortens up your code.
  • It stops your namespace from being polluted with extra function definitions.
  • In some cases it can be faster than defining an auxiliary function.

I also like them because when I'm coding on the fly I don't have to break out of my input cell, define a new function, then come back to the cell and use the new function. Essentially it allows for a “code as you think” approach, if that makes sense.

Let's make up a problem and say you want to select integers whose square + 19 is prime.

We can do this without pure functions, but like I said, the workflow is not as smooth if you didn't know before hand you needed an auxiliary function. This can be done like so:

specialPrimeQ[n_] := PrimeQ[n^2 + 19]

Select[Range[100], specialPrimeQ]
{2, 8, 12, 20, 22, 30, 40, 42, 62, 68, 70, 98}

Now here we had to define a function, which now exists in the global namespace. If this function is never used anywhere else in your notebook/codebase, it might be a good idea to find a workaround that doesn't define an entire new symbol.

One such workaround is to inline things with a pure function:

Select[Range[100], Function[n, PrimeQ[n^2 + 19]]]
{2, 8, 12, 20, 22, 30, 40, 42, 62, 68, 70, 98}

This is quite verbose though. We can make it a bit smaller by not giving the argument a name. The default name for an unnamed input is # (read as Slot):

Select[Range[100], Function[PrimeQ[#^2 + 19]]]
{2, 8, 12, 20, 22, 30, 40, 42, 62, 68, 70, 98}

This is still verbose. There's shorthand for the Function head and it's &:

Select[Range[100], PrimeQ[#^2 + 19]&]
{2, 8, 12, 20, 22, 30, 40, 42, 62, 68, 70, 98}

With this solution, I didn't pollute my namespace and I didn't have to jump out of my input cell. So in some sense it makes things both cleaner and quicker.

At the end of the day though, it's up to you to use them or not. I think it's important to understand them because they're a ubiquitous part of the language. I highly recommend using them when they're short and not using them when they're long, as these can be harder to read.

A time you might not want to use pure functions is with conditional definitions. Consider:

f[x_?Positive] := x^2
f[x_] := -x^2

f /@ Range[-3, 3]
{-9, -4, -1, 0, 1, 4, 9}

A pure function would involve extra constructs since they don't have pattern based definitions:

If[# > 0, #^2, -#^2]& /@ Range[-3, 3]
{-9, -4, -1, 0, 1, 4, 9}

Though, there's usually a way to make things cleaner. In this case:

Sign[#]*#^2& /@ Range[-3, 3]
{-9, -4, -1, 0, 1, 4, 9}

As for speed, pure functions tend to be faster than their DownValue analogues. For a simple function definition, the pure function is 2.6 times faster (on my machine) since it has less overhead:

g[n_] := n

g[100] // RepeatedTiming
{2.6*10^-7, 100}
#&[100] // RepeatedTiming
{1.0*10^-7, 100}

Though I will say that this overhead becomes much less of a concern if the body of your function is much more time consuming.

As mentioned by b3m2a1 in the comments, pure functions can auto-compile easier. Compare the timings here:

func[x_] := x^2 + 1

func /@ Range[100000]; // AbsoluteTiming
{0.109859, Null}
#^2 + 1& /@ Range[100000]; // AbsoluteTiming
{0.002947, Null}
  • 7
    $\begingroup$ Worth mentioning that pure functions can auto-compile better $\endgroup$
    – b3m2a1
    Jan 26 '18 at 19:28
  • $\begingroup$ @b3m2a1 Good thinking. Edited. $\endgroup$
    – Chip Hurst
    Jan 26 '18 at 19:56
  • $\begingroup$ @b3m2a1 and Chip, any comments about my closing action? $\endgroup$
    – Kuba
    Jan 27 '18 at 8:52
  • 1
    $\begingroup$ @Kuba I’m not sure, but I lean towards not duplicating this post. The first thread mentioned is asking a slightly different question: when to use pure functions, not why use pure functions. I don’t see how the second thread relates to this post. $\endgroup$
    – Chip Hurst
    Jan 27 '18 at 14:53

This site is temporarily in read only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .