How do pure functions differ from functions defined with SetDelayed?

You fail to mention the error message that is generated when one attempts to run g:

Function::flpar: Parameter specification {{a,b},{c,d}} in Function[{{a,b},{c,d}},(a z+b)/(c z+d)] should be a symbol or a list of symbols.

One "easy" fix is to write the pure function as follows:

Function[(#[[1, 1]] z + #[[1, 2]]) / (#[[2, 1]] z + #[[2, 2]])]

The message, as well as the documentation, makes clear the reasons for the error. In the syntax Function[{x1, x2, ...}, body], the top level elements of the list are considered the formal parameters of the function. The error message says that they should be a list of symbols, not a destructured pattern.

A definition like

f[{x_Integer}] := x + 1;

says to Mathematica: "Whenever you see an expression that comprises the head f applied to a list with one element, add one to that one element in the list and substitute the overall expression with the resulting value." The important thing to note here is that the arguments inside the square brackets comprise a pattern.

On the other hand, the slots of a pure function are not patterns--even though they may be matched to patterns before deciding on their final evaluation. The expression above is equivalent to the following pure functions:

Function[If[MatchQ[#, {_Integer}], #[[1]] + 1, #]]
(* or *)
Function[Replace[#, {x_Integer} :> x + 1]]

or any number of equivalent forms.


You'll be much happier just using Mathematica the way it's designed to be easiest to used I think... but if you really would like this, we can do some operator overloading with a symbol other than Function (overloading Function like this is dangerous and not worth it)

LongRightArrow~SetAttributes~HoldAll
LongRightArrow /:
 Set[
  name_,
  LongRightArrow[a_, b_]
  ] :=
 Replace[
  (Hold[a] /. 
     s_Symbol?(Function[Null, Context[Unevaluated[#]] == $Context, 
         HoldFirst]) :> pattern[s, Blank[]]) /. pattern -> Pattern,
  Hold[pat_] :> SetDelayed[name[pat], b]
  ]

Then:

f = {
   {a, b},
   {c, d}
   }⟶((a z + b)/(c z + d))

f // DownValues

{HoldPattern[f[{{a_, b_}, {c_, d_}}]] :> (a + b)/(c + d)}

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

(2 + z)/(4 + 3 z)

Or you can alternately define some wrapper like:

bindDV~SetAttributes~HoldFirst;
bindDV /: HoldPattern@Set[name_, bindDV@Function[a_, b_]] :=
  Replace[
   (Hold[a] /. 
      s_Symbol?(Function[Null, Context[Unevaluated[#]] == $Context, 
          HoldFirst]) :> pattern[s, Blank[]]) /. pattern -> Pattern,
   Hold[pat_] :> SetDelayed[name[pat], b]
   ];
bindDV /: HoldPattern[Function[bindDV[a_], b_]] := bindDV[Function[a, b]]

g =
 bindDV@{
    {a, b},
    {c, d}
    } \[Function] (a z + b)/(c z + d)

And

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

(2 + z)/(4 + 3 z)

If you want to work with this in a slightly less visually obtrusive way you can also add a rule like:

SubStar /: HoldPattern[Function[SubStar[a_], b_]] := bindDV[Function[a, b]]

And then you can do things like:

h =
 SubStar[{
   {a, b},
   {c, d}
   }] \[Function] (a z + b)/(c z + d)

Which visually looks like:

enter image description here


This is possible along the lines of this answer, if we generalize it a bit, and with all warnings and caveats mentioned there. Here is the code:

SetAttributes[validCompoundArgumentQ, HoldFirst]
validCompoundArgumentQ[_Symbol] := True
validCompoundArgumentQ[{___?(Function[x, validCompoundArgumentQ[x], HoldAll])}] := True
validCompoundArgumentQ[___]:=False


Unprotect[Function];
Function[       
   {left___, compound_List /; validCompoundArgumentQ[compound], right___},
   body_,
   atts_ : {}
]:=
   Module[{var},
      With[
      {
         rules=
            ReplaceAll[{p___Integer} :> var[[p]]][
               Flatten[
                  MapIndexed[                         
                     RuleDelayed,
                     Replace[
                        Unevaluated[compound], s_Symbol :> HoldPattern[s],{0,Infinity}
                     ],
                     {-2}
                  ]
               ]
            ]
      },
         Function @@ ( Hold[{left,var,right}, body, atts] /. rules )
      ]
   ];
Protect[Function]

You can consult the linked post for the details of how this works, but basically this allows one to use arbitrary nested destructuring in Function. For example, the following function:

Function[
  {{a, b}, {c, d, e}, {f, {g, h}, i}}, 
  a + b + c + d + e + f + g + h + i
]

evaluates then to:

Function[
  {var$69225, var$69226, var$69227}, 
  var$69225[[1]] + var$69225[[2]] + var$69226[[1]] + var$69226[[2]] + 
  var$69226[[3]] + var$69227[[1]] + var$69227[[2, 1]] + var$69227[[2, 2]] + var$69227[[3]], 
  {}
]

For your particular example, you will need to wrap the arguments in an extra list, so that Function does not interpret them as being two arguments:

{{{a, b}, {c, d}}} \[Function] (a z + b)/(c z + d)

(* 
   Function[{var$69234}, 
     (var$69234[[1, 1]] z + var$69234[[1, 2]]) / 
     (var$69234[[2, 1]] z + var$69234[[2, 2]]), 
     {}
   ]
*)

and

({{{a, b}, {c, d}}} \[Function] (a z + b)/(c z + d))[{{1, 2}, {3, 4}}]

(* (2 + z)/(4 + 3 z) *)

I think this mechanism is reasonably safe, but OTOH overloading such a core construct as Function can be dangerous (this puts it mildly). So this can be fine for personal interactive use, but I would not use this method for any code I would distribute to others, or used in some kind of production environment. See the linked answer for safer version of this approach, that does that locally, using Internal`InheritedBlock.