21
$\begingroup$

Mathematica allows to define pure function, like

Function[{a, b},Length[Unevaluated@a]{b}][1+2,2+3]
(*
==> {0}
*)

Pure functions in Mathematica can take an arbitrary number of arguments, but only if not naming them, for example:

Function[Length[Unevaluated@#1]{##2}][1+2,2+3,3+1]
(*
==> {0,0}
*)

Also, pure functions can optionally have attributes, for example:

Function[{a,b},Length[Unevaluated@a]{b},{HoldFirst}][1+2,2+3]
(*
==> {10}
*)

However what I haven't found is a way to have both arbitrary many arguments and attributes:

Function[(* what, if anything, to put here? *)][1+2,2+3,3+1]
(*
==> {10, 8}
*)

Therefore my question:

Is it possible to define pure functions which take an arbitrary number of arguments and at the same time have attributes? And if so, how would one define them?

The obvious solution doesn't work:

Function[Length[Unevaluated@#1]{##2},{HoldFirst}][1+2,2+3,3+1]
(*
Function::flpar: Parameter specification Length[Unevaluated[#1]] {##2} in 
Function[Length[Unevaluated[#1]] {##2},{HoldFirst}] should be a symbol or 
a list of symbols. >>
*)

Adding an empty parameter list disables parameter substitution for ##;

Function[{},Length[Unevaluated@#1]{##2},{HoldFirst}][1+2,2+3,3+1]
(*
==> {##2}
*)

Of course, a workaround is easy; for example, have the pure function take a list (which in the example above would actually have been the better alternative anyway), or simply using a named function. So it's more of a curiosity. It just seems odd to have two completely orthogonal features of pure functions, and yet not being able to combine them.

$\endgroup$
  • $\begingroup$ You could do something like Function[, ##, <Attributes>], for instance. Is that what you were asking? $\endgroup$ – rm -rf Jul 25 '13 at 17:25
  • 3
    $\begingroup$ Try Function[, {Unevaluated@#, Unevaluated@#2}, HoldFirst][2 + 2, 2 + 2]... $\endgroup$ – ciao May 26 '16 at 22:17
  • 4
    $\begingroup$ Attributes are documented now: ["Function[params,body,{attr1,attr2,…}] represents a pure function that is to be treated as having attributes for the purpose of evaluation."](reference.wolfram.com/language/ref/Function.html). HoldAll and Listable are used in the examples. $\endgroup$ – Karsten 7. May 26 '16 at 22:30
  • 1
    $\begingroup$ @Karsten7.- the attributes have been documented for a long time, I think the OP means in the context of using them with Slots for arguments. $\endgroup$ – ciao May 26 '16 at 22:39
  • 1
    $\begingroup$ No, I meant what @ciao mentioned. This is a different form from what was mentioned by Karsten_7. $\endgroup$ – Leonid Shifrin May 27 '16 at 9:10
23
$\begingroup$

Yes, this form exists, and was first shown to me by Leonid. It is:

Function[Null, (* body with ## *), (* attributes *)]

As always the Null may be implicit, so in your application:

Function[, Length[Unevaluated@#1]{##2}, HoldFirst][1+2,2+3,3+1]
{10, 8}
$\endgroup$
  • 8
    $\begingroup$ +1. To be pedantic, I will add that this form is undocumented (but very unlikely to be discontinued). $\endgroup$ – Leonid Shifrin Jul 25 '13 at 18:02
5
$\begingroup$

This is the answer merged from this more recent question

The form in question

I meant the form

Function[Null, body-using-slots, attrs]

as ciao correctly noted.

At least at the time when I wrote the book, this form hasn't been documented. I learned about it from Roman Maeder's book "Programming in Mathematica". OTOH, this form is very useful in some cases, particularly with Hold - attributes.

Use cases

I can think of two major classes of use cases for this form

  • Cases, when we could get away with using named arguments, such as

    Function[{x,y}, x = y, HoldFirst]
    

    but prefer not to, because of scoping problems / leaks associated with this form. Basically, the mentioned leak makes passing such functions into other functions generally unreliable. And it does happen in practice, I was bitten by this many times, most recently just a couple of weeks ago. This problem isn't there for Slot - based functions, although the latter have more limited nesting capabilities.

    Note, however, that in modern versions of Mathematica, a new system option "StrictLexicalScoping" is available, thanks to Daniel Lichtblau, which solves this problem. But, for certain reason, it is not yet a default, and if you write code for others, you probably can't count on it being set to True on their machines, so having alternatives is still useful. This option has been discussed a few times here on SE.

  • Cases, where we don't know the number of arguments in advance, and also where this number may not be fixed, but varies from call to call. Here is an example of such a function, written by Mr.Wizard in this answer: it computes the length of passed first argument, without evaluating it, and multiplies the list of rest of the arguments by it:

    Function[Null, Length[Unevaluated@#1]{##2}, HoldFirst]
    

A few more examples

Here is another general example I found, from the question MapThread with non-rectangular lists, the solution given by Rojo:

Function[Null, f[##], Listable] @@ A

I also found a couple of my own posts containing this form: here I used it in this block of code:

MapThread[
   Function[Null, Hold[#1 = #2], HoldAll],
   Unevaluated @ {vars, vals}
]

which generates held assignments to variables without evaluation leaks, and here, where the following pattern

s_?(Function[Null, ListQ[Unevaluated[#]], HoldAll])

was used to test for an argument having the head List, without evaluating it.

Summary

The form in question is

Function[Null, body-using-slots, attrs]

which is really useful in a number of circumstances. The use cases for it divide into two large categories:

  • Cases where Function with named arguments can be used in principle, but we still prefer the slot-based function. One of the main reasons for this is to avoid lexical scoping issues.

  • Cases where Function with named arguments can not be used even in principle, simply because the number of arguments is unknown at function's construction time, or can vary from call to call.

$\endgroup$
  • $\begingroup$ I found you name from the list of all time top users of the combinatorics tag. So may be you can help me with this question. Sorry for post this request post as a comment. $\endgroup$ – Bumblebee Jan 27 '17 at 23:59
  • $\begingroup$ @Nil Sorry, can't help. No spare time for foreseeable future :(. If you really need a good answer, try to do part of it on your own, learn basics of Mathematica & Maple, etc., and post questions with specific issues you face - in this way, people will be more willing to help. $\endgroup$ – Leonid Shifrin Jan 28 '17 at 0:55
  • $\begingroup$ @Nil the Maple code you linked to, isn't small, and the algorithm would require people to read and understand the paper. Besides, only a small fraction of people here are fluent in Maple. All in all, answering your original question is no small project. Most regulars here just don't have that much spare time on their hands. OTOH, if you try encoding that algorithm in Mathematica (presumably you want Mathematica solution), and are stuck at specific point(s), people will be happy to help as long as you isolate the problem and not dump a huge code sample on them. $\endgroup$ – Leonid Shifrin Jan 28 '17 at 1:04
  • $\begingroup$ Thank you for your detailed explanation and now I understand the situation. Blindly I put a huge Question in front of people in here. Anyway, finally I learned that already there is a such Mathematica code (by a comment on my question). Maybe I can do something along with this hint. Thank you very much :) $\endgroup$ – Bumblebee Jan 28 '17 at 4:17
  • $\begingroup$ @Nil Glad if that was helpful. Good luck with your pursuits! $\endgroup$ – Leonid Shifrin Jan 28 '17 at 16:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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