In this answer, I focus on the case of pure functions with one or more Slot in their body, which is different than the other answers.
First I present the function optionalFu which works just like Function, but which turns of a message. This message is the one that is generated when not all slots can be filled from the given input, like in {#,#2}&[5]. It is interesting to interpret this message as a warning, rather than an error and to ignore it, because Mathematica still fills all the slots it can. So {#,#2}&[5] evaluates to {5,#2} and it remains to be seen what to do with #2. The second function in this answer, optFuWithDefaults, replaces such slots (#2) with default values.
Be warned that the code for optionalFu and optFuWithDefaults are both very unreadable, but they appear to work nicely and it definitely more informative to read the examples below than the code.
optionalFu =
Function[Null,
ReplacePart[
Function @@ Hold[
Null,
Quiet[## &, {Function::slotn}],
HoldAll
]
,
{2, 1, 0} -> Function @@ Hold[##]
], HoldAll]
optFuWithDefaults =
Function[{functionBody, slotDefaultRules, nSlots},
Module[
{fuToken, argsHeld},
argsHeld = Hold @@ Slot /@ Range[nSlots] /. slotDefaultRules;
ReplaceAll[
Function[Null,
(Function @@ {
Null,
fuToken[##],
HoldAll
}) @@ argsHeld, HoldAll]
,
Join[
{fuToken -> optionalFu @@ Hold @@ Unevaluated@functionBody},
OwnValues@argsHeld
]
]
], HoldAll]
There are three arguments in this last function: {functionBody, slotDefaultRules, nSlots}.
functionBody is really a list (or you can use any head) of arguments that you would normally pass to Function. So if we set functionBody -> {{#, #2}} then this corresponds to {#, #2}&, while functionBody -> {Null, {#, Hold[#2]}, HoldAll} corresponds to Function[Null,{#, Hold[#2]}, HoldAll].
slotDefaultRules is a list of rules that sets default values for slots, if they are not present.
nSlots is the number of slots that are present in functionBody. I have chosen not to infer this from functionBody, because there may be functions inside functionBody and I don't want to mess with those. Instead, the user has to specify nSlots.
Simple example
fu1 = optFuWithDefaults[{{#, #2}}, {#2 -> 3}, 2];
fu1[1]
fu1[1, 2]
{1,3}
{1,2}
To quite some extent, functions in the functionBody argument are preserved.
fu2 = optFuWithDefaults[{{#,#2, {#,#2+5}&[#2,5]}}, {#2-> 3},2];
fu2[1]
fu2[1,2]
{1,3,{3,10}}
{1,2,{2,10}}
It also works with HoldAll and RuleDelayed
fu3 = optFuWithDefaults[
{Null, {#, Hold[#2]}, HoldAll},
{#2 :> Print[hello]}, 2];
fu3[1]
fu3[1, Print[goodbye]]
{1,Hold[Print[hello]]}
{1,Hold[Print[goodbye]]}
When using Rule and when not using HoldAll, arguments are also evaluated at appropriate times
fu4 = optFuWithDefaults[{{#, Hold[#2]}}, {#2 -> 2 + 2}, 2];
fu4[1]
fu4[1, 5 + 4]
{1,Hold[4]}
{1,Hold[9]}
As mentioned, you can use any head in the first argument (functionBody). This means you can also use Function and get a bit of syntax highlighting (but only in the first argument)
fu5 = optFuWithDefaults[
{#, #2} &, {#2 -> 3}, 2];
fu5[1]
fu5[1, 2]
Function[{u, v}, u^2 + v^4 /. {u -> 1, v -> 1}]it is not the same as with optional arguments bacause one need to put coma:f[,]. Also, it seems to work well with Numeric arguments while for others ("a" for example), precedences are different. – Kuba Jul 26 '13 at 08:16f[1,2,,2,,3], what is not so easy with standard optional arguments. – Kuba Jul 26 '13 at 08:23Nullissue you have faced lately? – Kuba Jul 26 '13 at 09:19