Why is tcl broken?
Pierre R. Mai
pmai at acm.org
Thu Jul 1 09:19:59 EDT 1999
More information about the Python-list mailing list
Thu Jul 1 09:19:59 EDT 1999
- Previous message (by thread): Why is tcl broken?
- Next message (by thread): Why is tcl broken?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Paul Duffin <pduffin at mailserver.hursley.ibm.com> writes: > If they are statically scoped how does the following work. > > Look at the following code. > > (let ((a 2)) > (let ((b 3)) > (+ a b))) > > When you expand the macro let you get something like. > > ((lambda (a) > ((lambda (b) > (+ a b)) 3)) 2) > > Which is equivalent (apart from side effects) of. > > (defun inner (b) > (+ a b)) > > (defun outer (a) > (inner 3)) > > (outer 2) They are _NOT_ equivalent, because the reference to a in inner is now completely free, which it was not in the lambda formulation. When you change the nesting of functions/forms like this, you of course change their meaning (this is basic Lambda-calculus). > So what is the process which associates the use of "a" inside inner > with the formal argument of outer. I In lexical scoping there is no such mechanism, since a variable has to be lexically apparent, for it to be accessible. Since a is not lexically apparent in inner, it is not accessible. That is one of the important features of lexical scoping, since it ensures that I only have to look at the lexically enclosing environment to understand which variables are accessed. Common Lisp allows dynamic scoping for certain, specially declared, variables. In CL you _could_ write the following as a somewhat equivalent form of your TCL code below, but no Lisp programmer would do it that way: (defun inner (b) ;; Note that a is a free reference in here, so we rely on there ;; being a dynamic binding in effect for a when we are called. ;; This is clearly insane ;) (+ a b)) (defun outer (a) (declare (special a)) (inner 3)) (outer 2) => 5 If you really wanted to do something this way in CL, then a would be considered a global parameter to the behaviour of inner, and would be declared prominently as such. If that isn't the case (as in your example, where arguably a is just a parameter of inner that you don't care to pass explicitly), you would simply use closures/nested functions as appropriate, or indeed pass the argument around, if you want inner to be self-sufficient. I.e. either do a) (defun outer (a) (flet ((inner (b) (+ a b))) (inner 3))) b) (defun inner (a b) (+ a b)) (defun outer (a) (inner a 3)) or c) (defvar *inner-frobnication-parameter* 0 "This global parameter allows you to fine tune the frobnicator of inner in a global fashion. Please use with appropriate care, since too high value here might result in the sudden destruction of the whole of creation, which some would consider to be enormous lossage.") (defun inner (b) "Frobnicates b via a general purpose frobnication-algorithm. See also *inner-frobnication-parameter*, which gives the total bias for the algorithm. See Knuth, Vol 1, P. 169 for a specification of the algorithm and the parameters involved." (+ *inner-frobnication-parameter* b)) (defun outer (a) "Frobnicates 3 via inner, with a as the setting for the *inner-frobnication-parameter*." (let ((*inner-frobnication-parameter* a)) (inner 3))) > The Tcl equivalent is > > proc inner {b} { > upvar 1 a a; > expr {$a + $b} > } > > proc outer {a} { > inner 3 > } > > outer 2 Even though I'd shoot every programmer that wrote the Lisp code I first wrote above, it still seems to me to be a much better construct than the corresponding upvar in Tcl: a) With Tcl it seems to me that the caller has no way of knowing which of the procedures he calls might simply start poking at his variables. Worse: Since upvar can be done for any n levels, he will have to inspect _all_ of the procedures ever called while he is active, to ensure that they are not using any of his internal variables. This to me seems clearly insane: If I were maintaining the Tcl code above, and at some point decided that a was a misnomer and should be called first-of-two (or whatever else), I'd have to look at all procedures ever called to ensure none was relying on my having a variable called a. In Lisp the special declaration would at least warn me about the fact that someone somewhere was relying on me dynamically binding a. b) The fact that upvar has to specify at which level the corresponding variable is to be found, makes apparent it's hackish nature. Inner shouldn't have to know who exactly will provide it with the value for a. In Tcl I can't call inner via a mediating function, like this: proc master {a} { middle 3 } proc middle {c} { inner c } For this to work, I'd either have to modify inner (in which case the old call-chain via outer would cease to work), or I'd have to modify middle, to ensure middle passes along a. Now imagine master either calling inner directly or via middle (depending on some condition), and you get into serious trouble understanding and maintaining the various relationships and variable bindings involved. This seems to me to take the problems of dynamic binding to new levels (i.e. it somehow reminds me of assembler code poking around in a caller's stack frame, to avoid copying values into new stack frames). > As I have said before it is simply an explicit form of the Lisp > mechanism which allows the let macro to be implemented using > lambda. It is very rare that N is more than 1. To repeat it: dynamic scoping is in no way necessary or even helpful for implementing let via lambda. LET is directly expressible via LAMBDA in the lambda-calculus, which is lexically scoped. The same goes for Scheme, which doesn't have dynamic scoping either. What you are doing is performing invalid transformations on lambda-calculus expressions, going on to claim that since you need dynamic scoping to make those transformations work, that dynamic scoping is needed to allow LET to be expressed in LAMBDA. Had you any knowledge of Lambda calculus, you wouldn't make these ridiculous claims. BTW: The invalid transformations you perform are common-place in Tcl, because Tcl (like C) doesn't allow nested functions. > > You are very mistaken. Common Lisp macros are definitively not > > another language. They manipulate S-expressions, which are what Lisp > > Lisp macros manipulate S-expressions but the macro definitions themselves > are interpreted differently to Lisp expressions. The macro-definitions are interpreted exactly in the same way as any other Lisp expression, i.e. in (defmacro blabla (a b) (my-expression)) (my-expression) is interpreted in exactly the same way as (my-expression) would in any other context (with appropriate bindings in place of course). > A simple macro version of setq would convert from > (setq symbol '(list)) > to > (set 'symbol '(list)) It wouldn't do this, since set can only access "global variables", whereas setq can access local variables. Take note please that set is deprecated, and that setq is not a macro, but a special form. If this doesn't mean anything to you, then please refer to the ANSI Common Lisp standard (also available in a hypertext version, at http://www.harlequin.com/books/HyperSpec), which would benefit this discussion greatly. > A macro setq is not interpreted the same way as set is because if it > was an error would occur when the Lisp interpreter tried to get the > value of the variable symbol before symbol was created. This doesn't make any sense. Please get your vocabulary right. The symbols in question are created at read-time, so there is no way that either of the above forms could be executed in an environment where the symbol in question didn't exist, unless you did some very evil hacking with the package system, the consequences of which I wouldn't claim to be well-defined. Now that I've re-read your sentence for the xth time, it seems to me that you are claiming that (setq symbol 5) is interpreted differently than (set symbol 5), which is of course right. That was the whole point of defining a macro for setq, that transformed (setq symbol 5) into (set 'symbol 5) and not into (set symbol 5). But this is not inconsistent in my book, because that is exactly what you asked for: You _wanted_ every (setq a b) form to be interpreted as if (set 'a b) had been written. So after macro-expansion, the evaluation of (set 'a b) is totally consistent with the evaluation of any other set form. If you find this inconsistency troublesome, than you have to condemn every kind of macro system, and Tcl, too, which allows the user to change the behaviour of commands. > I don't underestimate the power of Lisp macros I am just saying that I > find the way they warp the otherwise simple Lisp syntax / semantics. > > Lisp does not really need macros, they are really just a way of > creating short cuts. Firstly this sounds like the old argument against HLLs, i.e. we don't need high-level languages, they are really just a way of creating short cuts, which though true, totally misses the point of HLLs. Secondly, since special forms (of which setq as well as quote and lambda are prime examples) also warp the otherwise simple evaluation rules of Lisp, it can be argued that Lisp effectively needs at least a certain amount of warping, to be usable at all. Even the pure lambda calculus is warped in your eyes then. Funny how all the warpiness of Tcl doesn't grate on your nerves... > Tcl does not need a macro system because it has a simple consistent > syntax / semantics and all commands are treated equally and it is > possible to change the behaviour of existing commands. This is clearly no argument at all, because all the purported features of Tcl you mention are clearly orthogonal to the existence of a macro system. > I would say that Tcl (without macro system) can do anything that Lisp > (with macro system) can do. Assembler can do anything either Lisp or Tcl can do. This is no argument against either Lisp or Tcl. At the point where programming language discussions turn to this Turing-machine-equivalence argument, it is clear that the discussion is fruitless. end-of-line. Regs, Pierre. PS: Should any further discussion seems sensical (it doesn't to me), please heed the F'up to c.l.l, since neither Python nor Scheme are at all relevant to this. -- Pierre Mai <pmai at acm.org> PGP and GPG keys at your nearest Keyserver "One smaller motivation which, in part, stems from altruism is Microsoft- bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
- Previous message (by thread): Why is tcl broken?
- Next message (by thread): Why is tcl broken?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Python-list mailing list