Newsgroups: comp.lang.tcl,comp.lang.perl,comp.lang.scheme
Date: Wed, 11 Nov 92 00:21:57 EST
From: thayer@cs.columbia.edu (Charles Thayer)
Subject: Closures, Object-Oriented Equivalence, New version of Perl and TCL

[Back to the discussion of making TCL object-oriented...]

david@twg.com to comp.lang.TCL:
        dh> So, thinks I, why not create a way for multiple
        dh> interpreters to be active each of which thinking it owns
        dh> `.' ...
        dh> Since then I've realized this same stuff can be used to
        dh> create modules with a hidden implementation and public
        dh> interface.  It depends on using a separate interpretor to
        dh> hold the implementation, and puts tiny procedures into
        dh> each interpretor which wants to import the public
        dh> interface.

thayer@cs.columbia.edu to comp.lang.TCL,comp.OBJECT:
	me> You could implement the rest of an object oriented system by using
	me> interpreters as objects once you've built the glue to talk
	me> to all of them.

[continuing with my thought...]  This applies to almost all languages.
That is to say that if you can create a new instance of the current
language execution (or environment) you have a metaphor for an object.
In Perl (or ADA :) this would be supported with `package'.  In Scheme
it is a built-in concept.  [and I haven't even mentioned dictionaries
in forth or postscript.]  Having a new interpretOR that acts like a
normal run of the system is just like providing Perl's `package'
mechanism.  In fact, David Herron says at the top of his source for
the multiple-interpretOR: "An attempt at providing a `package' concept
in TCL".

Grady Booch [Object-Oriented Design with Applications] describes an
object as having state, behavior, and identity.  By creating an
individual TCL-interpretOR, you set aside a symbol table for holding
that object's state (eg. its variables.)  You also can create proc's
(procedures and subroutines) that can act upon that local state to
define that object's behavior.  And lastly, you have given that object
identity, by naming its TCL-interpretOR, and this identity can allow
you to uniquely access it later.

This however, only gives you an object, or more precisely a data
abstraction that includes local state and methods.  It doesn't give one
an object-oriented system.

Grady Booch describes object-oriented as having objects, classes, and
inheritance.  In order to extend the simple `package' model of objects
to form an object-oriented system, you'd want a way to distinguish
classes of objects (eg. a class state and interface that provide
functions on the class itself, such as new) and a way to inherit
between classes.  To support this through a `package' mechanism it is
important to be able to chain the packaged environments to control the
closure of an object.

For example, in Scheme, a 'define' saves the current environment and 
leaves the ability to reach the enclosing (dynamic) environment:

;; code from Scheme and the Art of Programming
(define (adder x) (lambda (y) (+ x y)))
(define add8 (adder 8))
(add8 3)
==> 11

Here add8 has it's y bound to 8 in the symbol table with x bound as a
free symbol; Whereas adder has x and y bound as free symbols.  By saying
the function is free, all I mean is that it is undefined in the
environment of that particular object.  When the free symbol needs to 
be resolved the enclosing environment is checked.  In this case one
could view '+ as being free in adder and add8, but bound in the top-level
environment (therefore not an error). 

In an object-oriented system this is a metaphor for inheritance.  An
enclosing environment can create a new sub-environment that could be
said to inherit the enclosing environment's state and behavior:

(begin
  (define (echo h . t)
    (if h (begin (display h) (echo t)) (newline)))
  (echo "Running")
  (define (ClassA)
    (define (p) (echo "class a"))
    (p)
    (define (ClassB)
      (p))
    (define (ClassC)
      (define (p) (echo "class c"))
      (p))
    
    (ClassB)
    (ClassC))
  (ClassA))
Running
class a
class a
class c
;No value

Here classB inherits from classA.  And classC inherits from classA,
but creates a new definition for p.  [Unfortunately, this simple
example is a bit contrived to avoid fun macros and evals.  Besides,
plenty of people have already written object systems in scheme :) ]

Likewise, perl has packages that have their own individuals stabs,
(aka symbol tables.)  However these don't nest (well).

#!/bin/perl
package main;
  @package=('main');
  sub moo { print "main'moo\n"; }
  &main'msg(moo);

package A; @package=('A', 'main'); # just pretend this is inheritance :)
  &main'msg(moo);

package B; @package=('B', 'main');
  sub moo { print "B'moo\n"; }
  &main'msg(moo);

[But since these environments don't nest, I kinda went and changed the 
usual calling semantics to a message based one]

sub msg {
    local($sub,@args)=@_;
    local($package,$filename,$line)=caller;
    local(@search)=eval "@$package" . "'package";
    local($context);
    while($context=shift @search) {
	eval "&$context" . "'$sub(@args)";
	return if (!$@);
	}
}

As one can quickly tell, it takes some doing before scheme or perl
would nest their environments in a way that particularly supports
inheritance.  In David Herron's interpretOR the problem of extending
the system to be object-oriented is identical to the one in perl, in
that environments are named at the global level.  One solution, hinted
at in the above perl code, is to have an environment to manage all
other environments (eg main and msg, tcl and interp.)  But this
quickly becomes tiresome to do by hand, and begs for a built-in
system.

As it stands, tcl is more well-behaved that Perl, but slightly less
than scheme.  Here is the same program (yet again) in tcl.  You should
note that variables nest in a very purposeful manner.

[thayer:0] BashER> tcl
tcl>cat object.tcl
#!/usr/local/bin/tcl

proc classA {} {
  set str a
  proc p {} { upvar str s; echo classA $s }
  p
  proc classB {} {
    uplevel p
  }
  proc classC {} {
    eval set tmp { [info body p] }
    proc p {} { echo classC }
    p
    eval { proc p {} } { $tmp }
  }
  classB
  classC
  p
}
classA

tcl>source object.tcl
classA a
classA a
classC
classA a

Oddly, tcl gives you full access to enclosing evironments with upvar/uplevel,
but is more prejudiced about making all procs global.  However,
because of the thoughtful design of tcl, I can just shove aside the p
proc temporarily to emulate the desired behavior.  Tcl could also be
used to do as perl does, except you'd want to save procs as strings
(another cumbersome system.)

David Herron's interpretOR (from my limited knowledge of its current
state) would be more nicely behaved and might look like:

interp new classA
interp new classB
interp new classC
interp eval classA {
  set str a
  proc p {} { upvar str s; echo [interp whoami] $s }
}
interp eval classB {
  proc p {} { interp eval classA { p } }
}
interp eval classC {
  proc p {} { echo [interp whoami] }
}
interp eval classA { p }
interp eval classB { p }
interp eval classC { p }

Here his closures handle partitioning of procs in a much better way
than writing a messaging system, in a way that is more compatible with
existing code.  However, one still yearns for a real object-system.

thayer@cs.columbia.edu to comp.lang.TCL,comp.OBJECT:
	me> This is very interesting.  It would appear that you've got
	me> yourself a simple closure mechanism.  And it has been said
	me> that "Objects are a poor-man's closures."

pdc@dcs.ed.ac.uk to comp.OBJECT:
	pc> I think this is an excellent and thought-provoking analogy.

I found it to be interesting too.  It's a proverb attributed to Norman
Adams, which I read in a recent article in "AI Expert" by Kenneth Dickey,
called "Scheming with Objects" [kend@data.rain.com].

throopw@sheol.UUCP to comp.OBJECT:
	tw> The *interesting* relationship is that the
	tw> essentially-syntactic sucrose making up the object(while)
	tw> captures an interesting subcase of the closure(goto), which
	tw> aids software developers in reasoning about a program's
	tw> behavior.

Absolutely. Which is why one would rather have objects than closures if
the language syntax can't be changed by the user.  However, it seems
that there is almost never a reason to not provide both.

Yet this begs the the two questions 

(a) "what is the exact definition of a closure in a language?" so that
we can know how to provide them.  I can tell you about closures in
mathematical systems, or explain why certain movies have good closure,
or point to lambda in some dialect of lisp and say closure, but I
don't know of any good discussions about them in milieu of programming
language construction --I imagine this is my own fault since I am more
interested in objects.  It seems to me that the closest thing to a
formal discussion of the operations on closures in a programming
languages is object-oriented-design  8-?

(b) "what is the exact definition of an object oriented system?" so
that we can know how to build one.  I can easily say "take objects,
group them by class, and add inheritance" but so many issues exist
about what language features make a system usuable for a programmer
that any one implementation is doomed to exclude many such features.
How should one take a system supporting closures and build a usable
object system?

In fact, some would undoubtedly say that closures themselves are a
subset of continuations anyway (you know who you are :) and before we
know it, we are back to working with turing machines and electrons :(

What I would seek out of all this is the simplest most straight-
forward way to provide an object-oriented system (maybe BOB is the
answer --Doctor Dobbs.)  I see TCL or SIOD as lightweight enough to
provide object-oriented systems to other programming languages such as
Perl.  The idea being that a very efficient tiny object-system in tcl
or siod, with the right hooks in the parent language, could function
to make the parent language object-oriented (where wanted).

Objective-C did a good job of extending C in a straight-forward
manner.  C++ did a good job of extending C in a not-so
straight-forward, but efficient, manner.  I seek something that could
be used to extend all languages that don't directly generate
executable code, and that could be used as callable libraries in those
that do.  Something that could be embedded in everything from awk
to common lisp, cheaply.

My motivation/interest in this is a bit bizarre.  I would like to see
tcl in almost everything as the new meta-tool.  Under Unix, pipelines,
regexps, and tcp sockets have done a lot for allowing free and liberal
tooling.  An open-tcl-interface on applications and toolkits could
provide a new realm of intra-application communication and floating
toolkits.  For a description of what I mean see the end of this post.
[or search for the string "intra-application"]

bevan@cs.man.ac.uk to comp.OBJECT:
	bv> You could always use a language which has closure(goto) and
	bv> lets you the _user_ add the syntax necessary to represent
	bv> object(while) when and if they want it.  One example of
	bv> this would be Scheme using its (hygenic) macro system.

Absolutely.  [it is hygenic isn't it..never thought of it that way]
However, I often find myself (whether the language is scheme, perl, or
tcl) eval'ing something for the third or fourth time, and saying to
myself "enough's enough."  Scheme is good language for writing other
programming systems such as Prolog, LOOP, Ski, expert shells.  [I
don't know anything about LOOP or Ski, but Stephen Bevan wrote these
in scheme.]  Other languages such as Perl or Tcl, however, don't
provide such facilities in the language.  To change the syntax you
have to change the language. (roughly speaking.)

Besides, who wants to sit down at common lisp and write an
object-system when there is already CLOS.  Who wants to add networking
code to TCL when there is Perl.  Who wants to write for X using the
Athena Widget Set when there is WAFE.  Likewise, nobody wants to write
while from a goto, when they sit down to work in a language.

throopw@sheol.UUCP to comp.OBJECT:
	tw> But it has been discussed many times on comp.object, so if
	tw> I'm overlooking a FAQ, or pummeling a deceased equine,
	tw> please treat me gently.

``I would call you a sado-masochistic-equestrian-necrofeliac, but 
that would be beating a dead horse''  --anonymous

I see it's 3:36am here in New York, so I'm going to get back coding.
Here's a small present for those that read all the way to this point:

#!/bin/perl

# Prints a profile of most used words.  The actual work in progress
# is yet another mail pre-sifter.

$garbage='[ \t\n,.<>\[\]{}!@&^|]';
while(<>) {
    /$garbage*(.*)/; $_ = $1;
    @wordlist = split(/$garbage/);
    $wc{$word}++ while($word = shift wordlist);
}
unshift(@prof, "$wc{$word}:$word") while($word = shift @keys);
sub bynumber { $b <=> $a; }
@nwc = sort bynumber @prof;
print "$word\n" while ($word = shift @nwc);


/charles 
==============================================================================
Charles Thayer, Columbia University, Dept. CS, Tech-staff... my words only.
==============================================================================
PS. Gee, so much writing and only the tip of the iceburg, drat.

PPS. Please forgive my cross posting.  This has relevance to the various
requests for an "Object-Oriented Perl" [c.l.perl], the discussion of
"Multiple Interpreters in TCL / Spawning" [c.l.tcl], "objects and
closures" [c.object], and more scheme code than I intended
[c.l.scheme].

PPPS. Here is what I meant about intra-application communication and 
open-ended toolkits:

Imagine the following scenario where I am writing an nfs-client
filesystem that will allow users to safely have mail delivered to
their home directory.  Here's what I would be running:
------------------------------------------------------------
X
tvtwm or your favorite window manager
editor on my source
debugger on the programmer
xterm to recompile and test the program
latex to format the documentation
xdvi to view the documentation in a pretty window
editor on the documentation
xclock to point out how behind I am
mega-biff that tells me if I have mail or if any new netnews has come in
trn to look for info on nfs-bugs
archie to find other example nfs-clients
ftp to grab example source
xnetload reports the load on machines around the network
xcpustate tells me that I need twice as much memory
xwafeping gives me the thumbs down if a machine goes off the net
xwafenetmate gives the number of users on machines around the network
editor to read my mail
xterm on another host to test the nfs-client remotely
vmstat in an xterm to tells that I am about to blow my lid
------------------------------------------------------------

Now imagine that these all had dinky little tcl front-ends that spoke
some IPC or TCP.  Imagine these tools written in tcl with procs to 
interface to all the underlying utility:

============================================================
systat - daemon that has interface to load, vmstat, users and all the
fun system things like disk writes and reads, each as a tcl proc.

term-manager - lets you control multiple terminals as a group of windows
on as many displays as you like.  If one terminal overlaps another it 
could automatically move to fit in a programmed way.

editor - lets you bring up as many windows and buffers as you, with
multiple font support, and cross talking between buffers. (just like
lucid emacs :)

ftp-er - supports a little progress window to many ftp sites, and
remembers what ftp sites you've been to and what software you got, and
where you put it.

rfc-822 - a bunch of functions to process rfc-compliant mail

trn-manager - like trn but now you can apply brutal regexps to kill
off threads, and has nice bookmarking so that you can save messages
more easily.

xdvi - same as it ever was but reprogrammable, so now you have a that
print button you always wanted.

gdb-engine - follows a process here or there.

tcl-latex - same as tex or latex, but tells you where the current
token is in the parser.
============================================================

Okay, now imagine that all of these work together with a send command.
The send command allows processA to execute TCL-code in processB.

c-mode in my editor brings up a bunch of buttons on the top of my
buffer.  I hit the one marked compile and another window comes into
the foreground with make running on my file (the editor has just
executed "send buffer *compile* restart".)  The compile fails and the
whole compile screen turns red, and my source buffer now highlights
a miscreant line in my makefile (the editor has just executed "send
buffer-set *source* error on line 234 Makefile".)  

I fix the Makefile and the compile works.  Now I hit the run button on
the top of my c-code buffer, and gdb comes to the foreground running
my program and highlighting the line currently being executed in my
c-code.  The run of the program is actually being displayed in a window
run by the term-manager, and simultaneous on a read-only window on 
a friends xterminal.  The friend is a project partner, for whom I 
granted access to certain xwindows.

A piece of mail arrives for my account and a window pops up.  This
is because I have told my mega-biff to pipe my incoming mail through
the rfc-822 server then check for certain fields.  I "set from-match
'*[Uu]rgent*'" in my mega-biff.rc file so that the window would pop
up and beep a few times.  Incidentally, I have also put the following
in my mega-biff.rc file:

proc pop-up {
  send loudly editor pop-to-buffer *in-mail* from [extract msg $last msg]
  send editor send buffer *in-mail* { beep; beep; display-time }
}

The mega-biff doesn't know how to pop-up incoming mail, but I have
taught it to send it to my editor if it noticed an interesting from
line.

Interestingly, although there are three or four users on the machine,
there is only one rfc-822 running.  Every new connection is assigned
it's own "interp new rcf-822.$port.$user.$host" so that multiple users
share the same code, but not data.

I read the mail, it is from my project partner who says "joe has heard
of a package called hlfsd that implements an nfs-client."  So I go to
my archie screen, where I am foolishly executing "prog nfs", and hit
the button "new interp".  This brings up a new archie client (also
David Herron's interpretOR) where I type "prog { hlfsd* } on { prep
cs.columbia barkley.berk } do { pop-found-list }".  As you can imagine
"pop-found-list" sends the results of a search to my editor, or if I
have no editor running, I have programmed it to just send me mail.

This goes on for hours...of course. 

Every now and then my mega-biff notices a new news posting with the
word closure in the subject line and pops up (via the term-manager) a
terminal session running trn, and doubles the size of my xclock so
that I don't loose track of time.

Every now and again my xdvi window pops up, when my friend sends 
his latex output to both of our xdvi's to show me a new page of
our documentation.

I get bored of working on the code at hand, so I write a simple
network monitor shell script.  It allows me to add or delete hosts to
watch and it brings up an xwafeclock inside itself when a host goes
down to point out the time of death, otherwise it queries the remote
systat "send host moose.cs.columbia.edu tcl-agent systat load-5;
load-10; load-15" and draws a graph of load with the results.

ETC...Etc...etc...

PS.  I would want to write the "backends" to these things in perl if perl
had a good open-interface to things like tcl.

For those who read this far here is another small perl present, and
another work in progress.  It generates tvtwm menus from dns and yp
information.

begin 444 tvtwmrc.pl
M'YV0(T*\$)/&S0LX9>2P4:!@!(@S9=PD#$.G#(@V>4#0L4/G3ALY8T"$J4/G
M31N*:<:$8<,F#PL0=>:4F0."B),I+_/ >?FB#)TQ+\BDF4/'3!HV,T6Z(=-0
M(QJ+;-*LL5CR8<2)%4$4K"AG)IV"9RY&C"F2I$F4*EGF<<%P#)HW<%"0('.V
M8 \P PL&K>O&39@V9<"DV*$ ;D04-G&*>$L41 LVCNF 0%/0S!L0<_F"X",B
MQ68^"D"(!B'4HH@A;^JP(>/FA&2(DAE+KGS9LAS,=$\6%$'8L!L43ZI082'"
MAXN-'3^.<3&R)$2)<BB6(=.9L$RD8^@ %SY8 0D^/6(0;DKE*6F*8<2$D0GB
MC9G);QK3EG/RZQLW6FF*J7-4,ID\?K61TDHM@6"&'":1YL8<"MQ!&5(H\)"8
M#Y[M$=IH;L%%V&@@L/%&6G*Q(1L+)%#FAF4DSI'''"3&ET(/<\ 1E78O@/ "
MB5]TQZ&'()(PAVPOQC@C"B]PX<*-)(@8'QTZCC;7@E^H.,<>/LK61P\^KKBA
MDZS-\45\5/ZXY)4DQ$=8'PJ,X2%[B8WGD!1EA$&&4Q85! =)2LVY54)P="59
MC&6,D<9*((QATDE+,>C@465$2$005 1!(0@6<NC0% @)2F@8V:5Q7WX:A3$5
M?FF\%T:'!5DD1AEG%$13&$N!$!$9--V1!AUH@ #$A:(Y="I""A5Z**QZTF0;
M8'KBAZM%EK'TAJUNA'5'&'GHP"L(#@%A:!N(SAF&'&?$(!*X,H#@PKGCGN'$
MKI:"8,*VW:*0;@PO?7N&#"^=ZT*]X#H!0I.BE0H"D5X @8(+*Z0 ! DO5*A 
M CRN)!>\Q+( A+US!"GCK43:2&(,W4'\H<0DC$&4'&"]*(())0^[E+R\/1PQ
M&W+1T08<05)FAF07@SL'80F4?#)8/8@@-!TH1XN9S7#$G,"B$)+ -(PZ\XQQ
MA9B9C#311FN=]!DO27TS;R"@&;377!_]];\[<.&&TV7803+:T7K&!Q\@3"L'
M?B((4<41.F0]=+0Q<]B'K&RP9V$"#B5A*JH2@>K&&Y*=2G&LMX(:Z%O3/=SG
M5IA]L:5H:*)9!AZ9P^ F"$V(FM2I@+E1AZQN()V1;2#(44=?8"D% AY<M:' 
M''6(\7OPE%[KT++IUA$[':]VE3NKIQO[QFTC@B!E16UHI%,9]<8:G_<(73NS
M7/6Y]47V)*2/1I0KDNC^EQGW (3H38WV>>T@B. 0$V]XPQIZASL>M(\BZAO1
M 7_R/BG)#X'OBX\/W%:XT9P/"%-949"N)YD,S@$%)>@2_0#6H9'1K$1+(B'4
M+"(7V5"M5-K!8!DTZ+ $" P%1I.-"'K@!Z/-3X<UW)$)Y>(B+(D03"@D2A^ 
MQJ$;&BT^.^SA M4'Q1J*K$=2>M&3O"0E*ED):#9\#PZS-(<H^A""\"NC%3ED
M N E1$!^.2&0P'BX,B3.(A8:00(2P*']2<9_VEL1:=+ &M=<!((&NAX(#*C 
M^*1H11-\&QT?9CC$*:XA>_1C_QPR/J$44C+N2^1M&+FD%LTADDXSW,/J>,?D
MZ;&/2?NC0V0SR$\>DH&B7&02Z8!*H*%)E1P2CP+0-+SB@2 /R*N4!8=(@C,@
MJ XXLQ_^'**_6&Z23@]Y)AQ U4QM4G!T)>Q15(CR(C#H))3=3,TV)4('9ZH3
M#(2AIF@T"<AQSH8F21H*';YIOB$"P9X;E(-VA,2Q%VP!!%S8IQNZL (D ;0[
M\@0!/1UB3U#]4Y_\%&*/YGBM%0YLER_<&0HN2LX:MC%X!2$91X$Y&F$2DWC&
MRX-*DMG/'K'3G=!\T?W .5%LXG2;0\',3;TIR6OY!@59@ )Q=#)3H?KDIYPA
MX04!"LX51BBID]I#H=X"AQW 9$%5&ZD]<328LEV+I'2 $0=U9<]XPA)T]=2G
M1>WY30[55*4IK.J#6 C2'\%0K/I,00W/5Z44PFAC-#(2DCB:@).^,:4G%!,Y
MP?G+EIZ)(3 =)%%.0I2$)*^)8FS!;430DY\$19]&08H:DY< WPA%#HA)PA2H
M8(0D,*$(BBDM4(1"E-3.I#HR\^=K =J#KLCIM;&=;6UO.X60X0X%<^#@2(<;
MV" N$XM .NR0BG2DD&F49&:( VNT*%G)N  $)W#!"<R%&[XP<30W)$%X62.K
M.(3.BHZE#V3E4E[OCL9L9E-3?*:3!M@20;:TM2UN0R;,2MI1<:HK'6:-Z4;]
MQO&SU\4K.:7)4VN*@ LY7!*($QH"$%>X>RV8 V1B (,6.V8.QFL!?G;I&"I@
MAI8M,!X-'','$.RX!6W0#QOJ8)$5X*D%%E'(&UHU8]FX(#.ZP8\)1ES48%ZV
MF,8CGAN^$+O9*5,TA)72%V37!IWB[ZW\$T&7$2H"DVEO=Y@1,YFIO >W@7@(
M4P#!%';7 AJ\@(QC=MZ(BW*<6R&E@J+);QMPZ (5B. E(MBS&_K\9SD[[]']
MJX(;TH 'X*+YCWVP<U%?:LP E8'+8\$PF)DY/U.;N<-P73.(W:QES+3Z+V6@
MLYU/DV=)VQJ-IAZT&0I-AT.#4]$X],(6IO@^4W>AT9B.-)_]#&U(:YK3GJXF
M7$/M!GX2\PM?*((3B !N\E"&)D%MSEF^DI8"/0<KG7,(:DZ4AC/4(3KVP8]O
M<XD<C["E*06YU:"BH@>4W(=!J/'0;18GA.N1(2$)5Z0(Q, &3JU!! ]+@J'<
MT#J_0$0.2*@W&J)R!C3\D>)$%@&E^H<'.,B)1Q?O7U>H8U9B-N$^'T(#@@##
MVH;+X>%RB/AHA5#Q,5Q\F QQPANH8&@\[M$A=&G-G][2XZ\4NPSJD8/U;K,L
M]D"++G=@4 )$@ </R0GC8\>#@(V.=K(3Q QF:#L>9OC;AY$=47(?PYV((IVV
MEP0.#>\TXWZ'J!/4JB!@MSL>9.(3G==![NPT.W7L_O YK.'O&.]# I+^AI"7
MG.0F9^UI#OZ&0P]^X]%%2MX0_ZQ<IH8.=Y+,?5JB>,;C2G=YA[G<)0]YG_!>
M\70O(](5H/'[=#P,'R?"?>@P!:J+7NVZ5_SOTQ[Y-YP=^'FH>]K?'G?%NYP,
MT1\[%):4!#HHO2*9WSSQ-W[\CQMA^?VS 1Y8+(+!3^$D+-'*Q@_I\82PA>E7
M]WYI)G_T9W_X!QE6IWI9-P=L$01F 2<5]Q5V4 9P,@=IH =E,'A1P!]&-ST6
MB(%Y\Q3X$09R<Q3I@11L(07$8A)04'1E\#Q/!P),X!/8]'6MARC(YUDRPBD6
M$09PL#$SY2D+PA;%MVEFD ="D >:UCH_"!:#%Q-481XI\2EB\ 9X0!C-]RQ%
MV'X)$8.\M8,9P3Q3B!\;1P=AD"IRL'[&!RLY* ='4 8FX1-RD!$BP&(P@ <R
MT&(M  ,K  /UYQ .DA!ULG\X^'$/\08ST1!7\"UP, 3W%EUR$(-M\ 836"B/
MJ$A588,]%@8[XUD/-X:ED@=@H0!$4 9F,!)L0 =&L#N=\BG#MBIE  <*, 5Q
M0&1E@($ B!0Q*"8"I1%-!P(+J !0 "O0\89Q:#O]LX<M8(<89P4%1@=UL!*E
M:'E_US\Q@ <T@''#V!<),05FF!4B,#GTL1(8EX5WX(P"%8ULX 2XQB#>F!)K
MD!&+(P)4< 5-  )%R#IL^'&Y]R%'1TP.L1]T4!(+PA !21($"0(]  (>E "!
M4X:G(QF!8P:LF&\*( 0(>1_BLI .F3L!1 <=.6QK%FDS88$'AW$8.9#W42X]
MT)$(0CDAZ0(C204><06L%W8HF9%N, ,*Z9(?&9,C:9-+T7JKZ :MN" 8=Y$Z
MR9(=.89MX$P@,)$NX"%WD!!*J9([V9.!XY10*944:93YUC\/)UIG*!,R]@8M
M8!EC$!-)F9($N9$@T)0;%Y.@>(1729 \V9);.9=2.8D3R!!NJ9$*R2&B$3@)
M:!%1:2 N$!U#84?/8I6!Z08L29B)>9B%J9A4"9DZF9>4:9B_F)@B28EE )@Z
M"9<TP2&!HXF7.6R,*1-WN9*#>9JCD9HWN9HRF6HB()1@!P)%>93"%YF<*9N7
MJ9J@Z0)UF0>DB96FV9D>23FV.9)-,!1CD)-8.9G">9DO*1G%.9))<"+74Q]"
M2)UX&9O,F9W/B9M3L"+<DY0BH'QW\#8)H)<BP1(Q.0=C@" L 79NH  B4 5-
M$Y\=22#U>9^E-V2R* )($(?U)Y\"*I7VB9]* A@,(0)&$ ,+&J#TZ: $FG\B
MT(=]B'$4*@,7&C@-JI@/6J#]XZ$6RI]&, ,C.I]L,* 0FJ)\**(LFHT 2J(9
M:J(;"AD=RH<NRJ(U\*(E.FPGRJ$>FHTL:@-$NJ-&VJ,TN@)#RJ(WT*0QJJ$S
M^J,KP*0LB@-6*J,HJJ55:I!B(3O:$R@6J0"]&98B,):M609GF98?PI8*\&6*
M^8D;%XK7PIIE.9H1-6QJ&1/#MZ9"*'-]*F-RNI;"9Z=\VIA[Z@*!RB#$-))3
M4))"N*BA(0(W]P:5.@<FB90),&R'F:EXIF=PE@10@ 7U)Y*X26MP5@-^2*J]
M!F<V, -^&*JW::9M1A.U5JNQVC^E*FFO(@;3B:LCZ:HC2*P8!ZRSNB"D88JK
MFJNSLZMOAA\/UWW,:JK.6G)I$*W'RJMPQJW+RFO:2A-JL 9LX*VM"J[X<:[I
M*JOE>A&3HZZZBJSR^@;C&JR[0Q-P$ =R0*_3:J_]^J_P*JS: XT VS_V2A2/
M5[#["@)V@ =YH <)2ZVU%K$3FZ]YA@104$;&NJZ3 0?"EZTW1Q"J5;%N%F2>
MJK$@D%0R(0<3J'4H2Q,ZP;)$H$\GD! >RZKU2A.\10?C&@1C(%OU][&ZRBE!
M)1LC.WH+4GIE4'\A0';(DV(KUF(PX!CX,6^I9Q$M8&-:Z[0O%F-BT&,_%F3 
M.&1<>V2@&!(M, 3N@G$.08]E<'D(LFDF [52^T8O5K4N)F,@0 5SBS3WD1(T
MT;5_&[AU2[AAZQ@ZYAAFBW)<2[:.80;XD0/S9P/J01-&)ADM(!DF,*XG!@(R
M@*-1&[I4"P)V>+6&2P)(\ 2S];:9&KHU4*4)4+I3JV*H:[4UAAFM^[J?VQ0B
M( 5/\ 0V-@5(4 1,P 1X:[JXF[I8"P+"2[P+LP>]2P6'8[C12P736[W7"V.,
MZV..*V1$QF/@FY:5>[F9.[G20AE9D6-A07$6YQC,,CLM,#L(4A6_&[=&$ 2S
M)03#2P7+>[M\>[4D0+VN:[V[N[_]^[_;>\#=*[;E^[AH2[X_1KD@8+DQ@+GK
MH;XA>"M<*P;O6W1K(+\&0K_V2SF7H5LO<(I$487.^;O$! 1:AFIF"JN[(L.[
M0\.SXZLW;&HZ+!+$VL.X]L/7*L2 \</<:L2GMF;NJL0_W :3X\1K-K!2G&H,
M6\5FBK%ZL"L*\*TABZGDRK$[2VQ(H0! $+HB &W]P[$MD -6BS JUY^;UFF[
M,JD@J[)@7*HERR@@D*!$,<:C"@3(I+>["F2>6D9US!!>7+-URI^EZK()$;. 
MW'1F+%/201_]HQ,O*\DBD,A=#+(_BW$60JXW2Q0Y*[.X&L@_RUE<X<DCB;0T
MH;2B[,AU, 8JT<=+,LE71ZJ[PVE&$,!Z>[K.Z[=#4 5.D 188 2[6\S'G,R+
MFV,1++Z1"[Z;R\%B@"#O2<)*QF26.,?O\;L*V\MX$ 3 C,G"K+M^NY9S?"J&
MJ\Z<QL[>"\UE*\T4+!K5+%IV$+X=W+Y)YB'<[,YX<"K@W&;B+ 3EC&+-B\YD
M*,XQ9F, '6/QW+B&?+;CVP*2"P+W#+/ZO"C\G#O^7!#=S&G&,]  700'O;>Y
MV[<+/<=<Z]#BS+41'<T4/<T[AM%'IM$3S=%<V\]+!M( ;1$#+02ZLR"VPG:U
MF[?FG- J'=)X,"?M+,Y.'=/S/-/U;-.<B]-FJ]/:_-$KS6ES$M2P4G#=>M3,
M.\#/JQYN(-:[*P2PH@5G^,P2+<$5?=$9G<\YS;X[[=$]C1]HK=8PK,@@6VMA
M\!BS3*X&&P0MH+RI3,EGC#PBH&R#S0;/YFC](VEJ+,?8YLJ!#6=MT (4V\B&
M_;!-T ):$*V!C,;*UMEZ,-EQ;-F4C=ET/'Q>+,N@7:I^##VF3<F92A=U< 85
MM[)D+< IK;K6FAJ^O1Z%:V.\?=R>"M<R#;GU7-<;C==;O=>D8=R_31,#'16K
M(E!Y<-+GO-3<G1!TD!&&.][>[=Q3#=T63<TW;==93=U(IM?<C-[E#;O]TP:J
MT=US"-Y*3=P7L=\),8>[J]]LP-_F+=7ZS-Z2*]UW[<'5S<T&CN#XW69/012R
M [3!'<S__;QNX149OKL?CN%'IN 3S>#N?=7P72ORS=/_?.%T$.(#+11V,!3A
MN>%);=9^2^,V_BF&R^.?JMX+/L'MO6,.'M\0/M_;#-) 7J@#C09U0"OWX=\Z
MCA]0+N7X8;A7'EU9;N)R3=-6[1A8S>))[N(@O>6?,M"V<7%[9+L<7N6B-,*&
MN^9"?N)$+KGF&^;X/-UE3M\@3>=__<FZJIM$69&76MB$WF.$>I*+O<O!:XI=
M\2,@$ 3)6[2L">DS@0;\"2<'DNF6[@+0TA6=_B/\N8W0\>DNQXUIJ*FB6;3&
M*IJ;/A,7^+1[=.D?.)K!VZ>NWJBN*0( 5)7_ND>XFIFK7H2A^.G'R9^E:.Q'
M^.EX.KAV2:%S2A.?DNC1&JDL.NWM@1]2\)&?OCO8+@):$$!ML.LNH ?DSJ*J
MP0;C;A*?3I$L@>XF$>M\)U"?'NEF*%#\V0+\WN_^_N_1.CD'N@2VI=BU[@*5
M)[C(*0)14 5) ,#"/FRU>"NRC9[J^8*%G9Z=5>X'/ZJ:ZCP41^MD#7.PJZF<
M8B)087W4T>9D)WF. 4UD(!T@4 ,OEA86P9._*P).X!-W<#TCS'LLCP?5)R<E
M/P104 5Z]HTB7[IZ%Q-*[Q@0<8P$O@(QL(PP("XY#P4)808W!Y]D#2QF,(E2
M!J)8, 8G\9]!;_8^6/)EKWMI3_*!#IW26=C1:9]V5'$2D1H>V^AE+ )%D'U[
M3];!!_5P"!C(& .V.G\MM@*V"@/+6/*EN!]GP(]!3P9B@ >$+_4940,T8+DX
M< ,VX*&J:\$8/ .9F_-#L!)K&8'74[1,K_HECP2GD_IL4*Q'_11J!_M9OR2W
MXOID!P>\S[ES8-?Q;,$$>+5QCYO=>2P&]S:@O?S?V?R?[O$=IXX2E8-[7[J(
4DOF&3^"(?X=VN (T0 ..C_69IP#=
 
end
Newsgroups: comp.lang.tcl