Re: ParsedBoxWrapper's in packages

*To*: mathgroup at smc.vnet.net*Subject*: [mg115433] Re: ParsedBoxWrapper's in packages*From*: Leonid Shifrin <lshifr at gmail.com>*Date*: Tue, 11 Jan 2011 00:33:22 -0500 (EST)

This is not really that complicated. From the user's viewpoint, you can define a package where you put loadWithNotations function, and just load it before anything else, perhaps by auto-initialization. As for the requirement to wrap calls to InfixNotation inside packages like this, BeginPackage["Global`",{"Notation`","NotationTest`"}] InfixNotation[ParsedBoxWrapper["\[Element]"],myElement]; EndPackage[], this does not seem to be a big deal either, and can be further automated if needed. The only other thing the user has to do is to wrap the calls to Needs into loadWithNotations, which doesn't look like too much work for me. As for whether this is going to work in later versions, I think it should, because it uses rather fundamental mechanisms which are not very likely to change (including Notation`'s attachment to MakeBoxes/MakeExpression).The code for loadWithNotations is not ideal (for instance, the way it deals with possible Aborts, exceptions, or prior definitions attached to SetDelayed), but it also can be improved. So, let me put it this way: I'd surely use it myself for my purposes, but I'd hesitate to package it for other people's use, mainly because of manipulations with SetDelayed. Regarding other ways of doing it: there are several stages of code processing, each of which could be used to try getting what you want. If you only wanted to use this notation when defining a function but not when it is used, you could as well register your functions with some other function, and then post-process their DownValues after they were given, replacing \[Element] by something else. But since you also want to use this syntax when you call, I can only think of 2 options: parse-time redefinitions (this is what Notation` does, and I doubt you can find much better solution, at least conceptually, given that you will also have to use it from the FrontEnd), or run-time redefinitions. For the latter, I'd consider UpValues. They could solve a subset of your use cases, like this for instance: In[2]:= sum /: sum[a_ \[Element] b_List, expr_] := Sum[expr, {a, b}] In[3]:= sum[i \[Element] {1, 2, 3}, i] Out[3]= 6 Another advantage of them is that they are attached to your symbols (sum etc), not to \[Element], and therefore, unlikely to break other functions using \[Element]. Here you also don't have to worry about dispatching myElement to either MemberQ or Element, as you would in the Notation`-based approach .They (UpValues) are more limited, however, due to the depth-1 rule (you can not attach them to a symbol which is deeper in an expression). Regards, Leonid On Mon, Jan 10, 2011 at 11:01 PM, Yaroslav Bulatov <yaroslavvb at gmail.com>wrote: > Wow, that looks complicated....is it likely to work in future versions? > > At the core, I basically want > 1. Have \[Element] work like MemberQ for lists. > 2. Define functions that use f[a_\[Element] b_] syntax > 3. Be able to import this functionality without breaking behavior or > functions like Reduce that use "Element" built-in > > Is there perhaps a way to do this without Notation package? > > > > On Mon, Jan 10, 2011 at 8:54 AM, Leonid Shifrin <lshifr at gmail.com> wrote: > > Hi Yaroslav, > > > > I could offer some chain of hacks, which, while not perfect, seem to do > it. > > > > First, an example package: > > > > BeginPackage["NotationTest`"] > > > > mySum; > > myElement; > > plus; > > > > Begin["`Private`"]; > > > > BeginPackage["Global`",{"Notation`","NotationTest`"}] > > InfixNotation[ParsedBoxWrapper["\[Element]"],myElement]; > > EndPackage[] > > > > mySum[a_ \[Element] list_,expr_]:=plus[a,expr]; > > > > End[] > > EndPackage[] > > > > Note that we wrap the calls to InfixNotation into BeginPackage and > > EndPackage in > > some special way. The next ingredient is a pretty dirty hack, for which I > > will probably be > > flamed: > > > > SetAttributes[loadWithNotations, HoldAll]; > > loadWithNotations[code_] := > > Module[{oldSDdefs = DownValues[SetDelayed], temp, result}, > > Needs["Notation`"]; > > Unprotect[SetDelayed]; > > def : _SetDelayed /; (temp; True) := > > With[{dv = DownValues[SetDelayed]}, > > Unprotect[SetDelayed]; > > Clear[SetDelayed]; > > ReleaseHold@MakeExpression[MakeBoxes[def], StandardForm]; > > DownValues[SetDelayed] = dv; > > Protect[SetDelayed]]; > > Protect[SetDelayed]; > > AbortProtect[Catch[Catch[result = code, _]]]; > > Unprotect[SetDelayed]; > > DownValues[SetDelayed] = oldSDdefs; > > Protect[SetDelayed]; > > result] > > > > You must define this function in the Global` context before you load the > > packages. Now, assuming > > that you placed your package where Mathematica can find it (or appended > its > > directory tp $Path), > > here is how you use it: > > > > loadWithNotations[ > > Needs["NotationTest`"]; > > ] > > > > Let us now check the resulting definition: > > > > In[5]:= DownValues[mySum]//FullForm > > > > Out[5]//FullForm= > > > List[RuleDelayed[HoldPattern[mySum[myElement[Pattern[NotationTest`Private`a,Blank[]], > > > Pattern[NotationTest`Private`list,Blank[]]],Pattern[NotationTest`Private`expr,Blank[]]]], > > plus[NotationTest`Private`a,NotationTest`Private`expr]]] > > > > You can load several packages inside loadWithNotations. In each of these > > packages, you must isolate your > > calls to InfixNotation, as I did it here. Note that this will only work > for > > definitions (with SetDelayed). > > > > Now, how does this work. It took me some time to figure this out. The > > problem is that Notation sticks to > > MakeBoxes/MakeExpression, which it redefines. But normally, when we read > in > > packages (unlike when we enter expressions in interactive FrontEnd > > sessions), they are parsed by some different means, so MakeExpression is > not > > invoked. So, what I did here was to force the code to undergo > > MakeBoxes->MakeExpression transition during the time SetDelayed executes, > > thereby forcing it to trigger the mechanisms of Notation. We need > > ReleaseHold because Notation`'s version of MakeBoxes wraps the result in > > HoldComplete, while we want it to execute. The reason why we need to wrap > > things in BeginPackage / EndPackage with Global` is that Notation` checks > > the $ContextPath for what is referred there as initializationContext > > variable, which must be on the $ContextPath. > > > > I can not say anything about the limitations of this approach, but it > seems > > to work in simple cases. I haven't used > > Notation` package ever before, so it could be that I missed something > > important. > > > > Hope this helps. > > > > Regards, > > Leonid > > > > On Mon, Jan 10, 2011 at 10:39 AM, Yaroslav Bulatov <yaroslavvb at gmail.com > > > > wrote: > >> > >> I'm trying to turn my notebooks into stand-alone packages and I'm > >> running into problems with Notation package. In particular, I'm using > >> Notation package and InfixNotation, and when I save notebook as a > >> package, I get "ParsedBoxWrapper"'s and definitions using infix > >> notation fail to match, for example. > >> > >> InfixNotation[ParsedBoxWrapper["\[Element]"], MyElement] > >> sum[a_ \[Element] list_, expr_] := ... > >> sum[a_\[Element] {1,2,3},a] > >> > >> This fails to match when in .m file, but works fine in .nb file. My > >> question is -- how should I package "sum" so that it matches correctly > >> both in packages and in notebooks? > >> > > > > >