11
$\begingroup$

I have a problem I'm trying hard to find a solution for, although I don't know if a solution exists.

The following rules produce the result e[2].f[]

x_G . f[] ^:= x
G[x_, ___, y_] := y[x]
G[2, e] . f[]

Would it be possible to get e[2] instead ?

I'm not sure it's possible as x in the left hand side of the definition of f is copied to the right hand side after having been evaluated. A solution would be to put a temporary HoldFirst attribute to Dot but this has undesirable side effects on the language.

Basically the usage I'm looking for is that an expression of the form G[x_, ____ , y_ ] is transformed to y[x], but not if it's part of an expresion of the form G[____].f[___], where in this case an UpValue should first be used, but the DownValues transformation to y[x] can happen in the rhs of f.

Would analyzing the stack help maybe? I tried that without success.

$\endgroup$
1
  • $\begingroup$ Related: (8558) $\endgroup$
    – Mr.Wizard
    Commented Feb 2, 2015 at 7:06

2 Answers 2

10
$\begingroup$

Yes, you can set both with UpValues:

x_G.f[] ^:= x
h_[a___, G[x_, ___, y_], b___] ^:= h[a, y[x], b]

G[2, e].f[] // Simplify
(* e[2] *)

It seems to work, but it is quite dangerous, as Leonid mention in the comment. For example, it was too easy to make a mistake in my previous revision.

Here Simplify helps to deal with pure G[x_, ___, y_].

$\endgroup$
8
  • 2
    $\begingroup$ This is indeed a possibility. I intentionally did not mention this, because patterns such as the second one here, are quite dangerous due to their generality. But, you have my upvote. $\endgroup$ Commented Feb 1, 2015 at 18:10
  • $\begingroup$ @ybeltukov , your solution works for my use case with this slight modification: h_[a___, G[x_, , y], b_] /; h =!= Dot ^:= h[a, y[x], b] . Thanks a lot for helping me, thanks to Leonid also for trying. $\endgroup$
    – faysou
    Commented Feb 1, 2015 at 18:51
  • $\begingroup$ I have quite a big project with such data structures as underlying objects as described in my post on structs in Mathematica mathematica.stackexchange.com/a/999/66, I have improved the ideas exposed on this site a lot, since posting them here, I will probably release a package publicly at some point. So your solution with my slight modification doesn't crash the system so it's very likely the right solution. $\endgroup$
    – faysou
    Commented Feb 1, 2015 at 18:54
  • $\begingroup$ Note that I never have pure patterns in my use cases, so Simplify is not useful in this case, although it's still interesting to know this. $\endgroup$
    – faysou
    Commented Feb 1, 2015 at 19:02
  • 1
    $\begingroup$ @faysou Well, this is surely a cool example of the power of UpValues, but I'd warn once again, that such constructs are fragile and an invitation to trouble (I tried this kind of stuff in my work more than once, and it always resulted in problems, sooner or later. Right now, I'd try to avoid such constructs for production code as much as possible). For example, for the definitions ClearAll[f, G]; x_G.f[] ^:= x;h_[a___, G[x_, ___ , y_], b___] /; h =!= Dot ^:= h[a, y[x], b], do you really want this behavior: Hold[G[2, e]] -> Hold[e[2]]? $\endgroup$ Commented Feb 1, 2015 at 19:42
9
$\begingroup$

General considerations

This seems to be tricky, and I don't know how to do this without involving the inspection of Stack. The main problem is the order of rule applications. Since Dot evaluates its arguments, UpValues for f or G are only applied after both f[] and call to G have been fully evaluated - and then it's too late. Therefore, generally you have 3 choices:

  • Make Dot be HoldAll. This is a bad solution.
  • Wrap the entire code you execute into some HoldAll dynamic environment eval, where you could implement some custom evaluation strategies. This might be the cleanest solution, if it is acceptable to you
  • Employ some tricks based on the inspection of Stack. This is the one I will illustrate.

Using Stack

Here is one possible way to use Stack. With this helper function:

ClearAll[stackAbove];
SetAttributes[stackAbove, HoldAll];
stackAbove[call_] := Stack[_] /. {before___, HoldForm[call], ___} :> {before};

You can define

ClearAll[G, f];
x_G.f[] ^:= Block[{G}, x];
call : G[x_, ___, y_] := y[x] /; FreeQ[stackAbove[call], HoldForm[call.f[]]];

Here, Block needed to disable the optimization employed by Mathematica evaluator internally to disable re-evaluation of already evaluated expressions, where the optimizer thinks that nothing has changed and there is no need to re-evaluate. In fact, already the necessity of this trick shows that we are trying to do something highly unusual.

So now:

G[2, e].f[]

(* e[2] *)

G[2, e]

(* e[2] *)

Remarks

I'd actually reconsider the design. Using Stack is a rather radical way to change the evaluation process, and having to use it tells us that we are starting to fight the system and swim upstream. There are usually better solutions within what can be achieved using the standard evaluation process.

$\endgroup$
1
  • $\begingroup$ The way you handle Stack is interesting in order to just get the useful information from the stack. $\endgroup$
    – faysou
    Commented Feb 1, 2015 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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