5

I'm trying to modify Plus but am running into trouble with it being Listable:

ClearAll[f, g, h]
Attributes[f] = {Listable};
h /: f[x_h, l_List] := 0
h /: g[x_h, l_List] := 0
f[h[1], {1, 1}]   (* {f[h[1], 1], f[h[1], 1]}, not OK I want 0 *)
g[h[1], {1,1}]    (* 0 as expected *)

How can I make the UpValue (or equivalent) have higher priority than the listability?

EDIT: I ended up wanting to do this again and figured I'd fix the Plus properly. Here it is working using Sashas answer and Mr.Wizards $Pre method:

    $Pre =.
ClearAll[myPlus]; Attributes[myPlus] = {Orderless};
Unprotect[InterpolatingFunction]; UpValues[InterpolatingFunction] = {};
InterpolatingFunction /: 
 myPlus[y_InterpolatingFunction[t_Symbol], l_List] := 
 Interpolation[
   MapThread[List, {y["Grid"], l + # & /@ y["ValuesOnGrid"]}], 
   InterpolationOrder -> First[y["InterpolationOrder"]]][t]
myPlus[other__] := +other
$Pre = Function[x, Unevaluated@x /. Plus -> myPlus, HoldAllComplete];
    Protect[InterpolatingFunction];

    y = Interpolation[Table[{i, RandomReal[{0, 1}, 2]}, {i, 1, 10}]];
    ParametricPlot[{y[t], y[t] - {1, 1}}, {t, 1, 10}]
ssch
  • 16,590
  • 2
  • 53
  • 88
  • You can't do that, or I'll be very surprised. Change your design somehow. It's hard to help in that without knowing a little bit about the bigger picture. Perhaps for your case adding the "listability" as e:f[_, l_List]:=Thread@Unevaluated@e or something similar, but that depends on the case at hand – Rojo Feb 05 '13 at 13:55
  • Oh, f is Plus, my bad. – Rojo Feb 05 '13 at 13:57
  • Yes, I agree. Not possible (I even tried with $Pre and $Post to fool the evaluator, but you cannot do that). Just program Listable like f[x_, l_List] := Thread[f, {x, l}]; and all is good. – Rolf Mertig Feb 05 '13 at 14:01
  • @Rojo Updated with bigger picture – ssch Feb 05 '13 at 15:37
  • 1
    I think this is one of these things which are really hard to make different generally and consistently, since one would have to explicitly go against the standard evaluation sequence. I would reconsider the design of whatever you try to achieve with this. While I will be the first to suggest workarounds which change system's behavior in many cases, I also think that admitting and accepting certain limitations of the system can sometimes be more productive. – Leonid Shifrin Feb 05 '13 at 20:48

2 Answers2

3

This seems disgusting, but here it goes

h /: Plus[x_h, l_List] :=
 withPlusListability[True][

  blabla; 0
  ]

withPlusListability[bool_: True | False] := Function[code,
   Internal`InheritedBlock[{Plus},
    Unprotect[Plus];
    If[bool, SetAttributes, ClearAttributes][Plus, Listable];
    code
    ], HoldFirst];

withH = withPlusListability[False];

So

withH[
  Print[h[3] + {4, 5}];
  Print[h[3] + 7];
 ];

prints

(*
0
7+h[3]
*)

While you evaluate code iniside withPlusListability[True|False], it takes care that Plus has|doesn't have the Listable attribute, without changing it globally. h's definition will only have a chance of matching with an unlistable Plus.

Plus is one of those symbols that are so special you really try not to mess with. As @Mr.Wizard warned, this will likely break for packed arrays, because it probably has been optimized to cut some corners.

Rojo
  • 42,601
  • 7
  • 96
  • 188
3

Contrary to Rolf Mertig's comment I believe $Pre does work.

First define your function:

h /: myPlus[_h, _List] := 0
myPlus[other__] := +other

Then set $Pre:

$Pre = Function[x, Unevaluated@x /. Plus -> myPlus, HoldAllComplete];

Test:

h[1] + {4, 5, 6}
0
z[1] + {4, 5, 6}
{4 + z[1], 5 + z[1], 6 + z[1]}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • @RolfMertig is pessimistic (comments) these days – Rojo Feb 05 '13 at 14:36
  • 2
    +other!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! HAAAAAAAAAAA +1 – Rojo Feb 05 '13 at 14:39
  • You make it look so easy, thanks! – ssch Feb 05 '13 at 15:59
  • @Rojo I'm afraid I don't understand your outburst. Unintentional humor? Does "plus other" mean something in contemporary parlance? – Mr.Wizard Feb 05 '13 at 16:36
  • @ssch Glad I could help. :-) – Mr.Wizard Feb 05 '13 at 16:37
  • Yeye, act cool, as if +something is everyday notation to you, terseness master – Rojo Feb 05 '13 at 16:44
  • @ssch I just realized I left out the huge limitation of this method: it only works on user input. I didn't intend this to be a complete answer but rather a response to Rolf's comment. My apologies if I made you think this was more than it is. – Mr.Wizard Feb 05 '13 at 16:45
  • @Rojo ah, that! I've been using that (in answers) for a while now so I didn't expect a sudden exclamation over it. At first I didn't use it as I figured it would be controversial but now I don't really care about controversy. :^) Later I'll find one of my earliest StackExchange uses of that form. I also use 1## as a shorthand for multiplication BTW. – Mr.Wizard Feb 05 '13 at 16:48
  • @Rojo after a bit of searching I think perhaps the first time I used it here was this post. I believe there are also posts in which I used +## but the search engine is fighting me. More recently I used it here. I've been using it much longer than this privately. – Mr.Wizard Feb 07 '13 at 09:24