Re: Add syntax highlighting to own command
- To: mathgroup at smc.vnet.net
- Subject: [mg101666] Re: [mg101539] Add syntax highlighting to own command
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Tue, 14 Jul 2009 05:32:55 -0400 (EDT)
- References: <200907090600.CAA17547@smc.vnet.net>
Hello all, I have a few remarks: IMO, solutions of Simon and Daniel are conceptually the same as Bastian's, but seem to me as yet another step towards brevity and elegance (this is subjective, of course). The major technical difference is that <If> is replaced by pattern-matching and thus there are two global rules instead of one, and in addition this allows to avoid Unevaluated. The mechanism that makes this work is however the same as before, since SetDelayed creates global delayed rules, and, as I mentioned before, RuleDelayed does not respect the scope of inner scoping constructs, which makes it all possible. The details of this have been discussed, in particular, in this thread (this is where I learned it): http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/a4db4903e8f994d4/9636cbfdeeed1fe2?hl=en&lnk=gst&q=scope+all+wrong#9636cbfdeeed1fe2 >Do our solutions differ in behavior? I wouldn't be surprised at all if >our codes behaved differently in the details. In most "normal" situations there probably won't be any difference in the result (except for the case of empty vars list, see below). But since the pattern - based function, in terms of evaluation, is different in several ways from the function with If (also If checks a different condition), it is not difficult to construct perverse examples where the results will differ. This is Bastian's solution: SetAttributes[Let, HoldAll]; Let[vars_, expr_] := If[Length[Unevaluated[vars]] == 1, With[vars, expr], (*else*) Unevaluated[vars] /. {a_, b__} :> With[{a}, Let[{b}, expr]]] Consider this, for instance: Block[{Length = 1}, Let[{a = "Hi, ", b = a <> "there!"}, Print[b]]] or Block[{If}, Let[{a = "Hi, ", b = a <> "there!"}, Print[b]]] Of course, no one in the right mind would do this, but this is just an illustration. By the way, the solution of Bastian returns an empty list when called with no variables: In[1] = Let[{}, a] Out[1] = {} which differs from what standard With or other two solutions do. Regardless of these issues, it seems (to me anyway) that when one tries to make a "production" code out of this simple and elegant solution, one really opens a can of worms. One thing is that argument checks and error messages have to be added, since without them Let is prone to misuse. This may seem unnecessary in simple programs but may probably give nasty bugs in larger ones, which will be hard to track. Here is my attempt in this direction (based on solutions of Simon and Daniel): In[2] = ClearAll[Let]; SetAttributes[Let, HoldAll]; (* Error messages *) Let::lvset = "Local variable specification `1` contains `2`, which is an \ assignment to `3`; only assignments to symbols are allowed."; Let::lvw = "Local variable specification `1` contains `2` which is not an \ assignment to a symbol."; Let::lvlist = "Local variable specification `1` is not a List."; Let::argrx = "Let called with `1` arguments; 2 arguments are expected."; With[{initPattern = HoldPattern[Set[_Symbol, _]]}, (* Main definitions *) Let[{}, expr_] := expr; Let[{a : initPattern, b : initPattern ...}, expr_] := With[{a}, Let[{b}, expr]]; (* Error - handling *) Let[vars : {x___Set}, _] := Module[{badarg}, "" /; Message[Let::lvset, HoldForm[vars], badarg = Select[HoldForm[x], Function[arg, ! MatchQ[Unevaluated@arg, initPattern], HoldAll], 1], First@Extract[badarg, {{1, 1}}, HoldForm]] ]; Let[vars : {x___}, _] /; ! MatchQ[Unevaluated[vars], {___Set}] := "" /; Message[ Let::lvw, HoldForm[vars], Select[ HoldForm[x], Function[arg, Head[Unevaluated[arg]] =!= Set], 1]]; Let[args___] /; Length[Hold[args]] =!= 2 := "" /; Message[Let::argrx, Length[Hold[args]]]; ]; This seems to catch errors in all cases which I tested (probably can be done more elegantly). But this reveals another, and IMO more serious, problem: Let does not nest nicely neither with itself nor with some (external to it) scoping constructs such as Function and With (Module and Block seem fine, at least upon the first look): In[3]:= Let[{a=1,b=2}, {Let[{a="Hi, ",b=a<>"there"},Print[b]], Print[a," ",b]}] During evaluation of In[3]:= Let::lvset: Local variable specification {1=Hi, ,2=1<>there} contains 1=Hi, , which is an assignment to 1; only assignments to symbols are allowed. During evaluation of In[3]:= 1 2 Out[3]= {Let[{1=Hi, ,2=1<>there},Print[2]],Null} We can also look at inputs like With[{a = 1, b = 2}, Let[{a = "Hi, ", b = a <> "there"}, Print[b]]] Function[{a, b}, Let[{a = "Hi, ", b = a <> "there"}, Print[b]]][1, 2] and see similar outputs. Contrast this with the behavior of nested built-in scoping constructs: With[{a = 1, b = 2}, With[{a = "Hi, ", b = "there"}, Print[a, " ", b]]] Function[{a, b}, With[{a = "Hi, ", b = "there"}, Print[a, " ", b]]][1, 2] where the variables of the outer constructs are shadowed by those in the inner ones. To be entirely consistent, one should probably add definitions to With, Function and Let itself, so that similar behavior will be observed in the first three inputs. But for Function and With, even if such definitions are implemented correctly, they will cause a (possibly quite large) performance hit. Of course, we may just say that most such collisions are pathologies and bad programming (bugs), and then ignore this problem. Regards, Leonid On Wed, Jul 8, 2009 at 11:00 PM, Bastian Erdnuess <earthnut at web.de> wrote: > I was missing a scoping construct like 'With' but where the local > variables get assigned linearly. E. g. > > In[1] := LinearWith[ { > a = "Hi, ", > b = a <> "there!" }, > Print[ b ] ] > > Out[1] = "Hi, there!" . > > I'm fairly new to Mathematica, but I know some Lisp, and somehow I got > it. I called my new construct Let and defined it as: > > Let[ vars_, expr_ ] := > If[ Length[ Unevaluated[ vars ] ] == 1, > With[ vars, expr ], > (* else *) > Unevaluated[ vars ] /. > { a_, b__ } :> > With[ { a }, > Let[ { b }, expr ] ] ] > > SetAttributes[ Let, HoldAll ] > > It seems to work fine so far. > > Now, I would like to have this construct load always when I start > Mathematica and I don't want to get it cleared when I use > 'Clear["Global`*"]'. So I put it in the System` context and also added > the attribute 'Protected'. I wrote all in a file Let.m and now, I > wonder where to put it that it gets read automatically at each startup > of Mathematica. > > However, my first question: Is it a bad idea to add things to the > System` context? And if not, where to put my file? And if, what would > be better? > > Second, my main question: Is it somehow possible to add this nice syntax > highlighting to the Let construct like with the With construct, where > the local variables appear in green? > > Third: Can I somehow add a help page? I have already the Let::usage. > > And last: Does anybody know how to make the construct better? Is there > something like syntax transformation rules in Mathematica? > > Thank you for your answers, > Bastian > >
- References:
- Add syntax highlighting to own command
- From: earthnut@web.de (Bastian Erdnuess)
- Add syntax highlighting to own command