[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon Jan 15 16:07:17 1993 
Subject:Re: lvars and standards (was: Re: List and Vector Syntax) 
From:Steve Knight 
Volume-ID:930117.01 

I was interested to read Brian Harvey's comments on why dynamic 
binding is attractive (in addition to lexical binding).  It is nice
to see someone try to defend the use of dynamic binding as a default.

The first point related to the ease of debugging:

> 1.  I find that debugging is easier for a beginner, who has no model of
> frames, bindings, and all that, in a dynamically scoped language.  When
> an error happens in Logo, the error handler just gives you a Logo prompt
> in the environment where the error happened -- and *all* the relevant
> variables in all the procedures higher up the stack are automatically
> available in the usual way. [Comparision with Scheme deleted].

This is a nice point, although more about the programming environment
than about lexical or dynamic binding.  To see that this is true, it should
be plain enough that inside an interrupt-context that there is a current
environment -- exactly the same as with dynamic binding.  And to access 
bindings deeper than the current binding requires the ability to redirect
context.

So my feeling is that this point boils down to the fact that it is easier
to implement a break-level interpreter with dynamic binding.  However, it
is just a matter of difficulty; there's not a great deal of difficulty 
doing this for lexical binding, after all.

The second point seems incorrect, as I understand it:

> 2.  It's easy to write control structures like WHILE in Logo because
> expressions are first-class.  You say
>       [LOGO code for while loop deleted]
> The parts in capital letters evaluate an expression given as argument.
> Under dynamic scope, the variables of the procedure that invoked WHILE
> are available.  To do something similar in a lexically scoped language
> you have to explicitly create and pass a closure instead of just passing
> the expression:
>
> 	Logo:  while [:x > 0] [make "x :x-1]
>
> 	Scheme: (while (lambda () (> x 0))
> 		       (lambda () (set! x (- x 1))) )
>
> It's the lambdas I'm objecting to.  (Yes, you can use macros so it looks
> nicer, but the Logo way is something a fifth-grader can understand.)

I don't see that this discusses the difference between lexical and 
dynamic binding.  It shows nothing more than the difference in syntax
for describing a nullary-procedure (a "block" in SmallTalk terminology).
In LOGO and SmallTalk, the [ ... ] syntax is used for denoting procedures
in a concise fashion.  By contrast, Scheme has no concise representation
for nullary-procedures -- and Pop is even more verbose than Scheme!

Whether or not there should be a concise syntax for denoting procedures
is an arguable point.  (In Pop, I suspect that most people would like 
a concise notation for procedures if an attractive and persuasive
candidate syntax was proposed.  None have made the grade so far.)  But
it doesn't help one distinguish between lexical and dynamic binding.

The more usual argument in favour of dynamic binding is its use for
reducing the arity of functions, such as -pr- the Pop printing function.
In POP-11, -pr- takes a single argument to print but is controlled by
seven variables in how it "renders" that argument (pop_pr_places,
pop_pr_quotes, cucharout, pop_pr_ratios, pop_pr_exponent, pop_pr_level,
pop_pr_radix.)  I'm not a fan of this kind of programming but it
certainly has its place.

I would like to agree with Brian in thinking that Ian's post was 
one-sided (though it was intended to be funny, too.)  However,
I regret that the argument is completely one-sided.  Dynamic binding
as the default binding for variables is an obsolete and dangerous
practice.  Pop is still in process of migrating from the old to the
new but no one doubts that it will eventually, fully change.

The choice of default binding in programming languages for children,
such as LOGO, has to be a poor choice.  It is extremely confusing
for children (and adult novices) to be faced with global/local
name shadowing.  It is even confusing for very capable and 
experienced programmers -- a colleague encountered exactly such a situation
yesterday.  Just for fun, here's the scenario

The programmer wanted to redefine the printing function locally.
So not unnaturally, he wrote

    ;;; SAVE YOUR WORK BEFORE TRYING THIS.   (see later)
    dlocal pr = my_pr;

The definition of my_pr was a stub.  For the purposes of exploration
he decided it would print the argument and then add the character
'X'.

    ;;; DON'T DO THIS ON YOUR WORKSTATION!!!  (see later)
    define my_pr( x ); lvars x;
        sys_syspr( x >< 'X' )
    enddefine;

What the programmer didn't understand is that string-append (><) invokes 
-pr- to find out the characters of the arguments.  The program promptly
recursed off to the limit of the call-stack and, owing to a weakness in
the POP-11 implementation of -pr-, core dumps.  So don't try this on
your workstation unless you've already saved your work!

Steve