this morning it struck me that it would be nice to have Parrot not only run Perl 6 and similar byte code, but that any of the common interpreted languages be compiled to this same byte code.
Then no matter whether running a Perl 6, Python, Ruby, Tcl maybe even bash script the same interpreter library would be used. Then likely it would already be in memory speeding up start time.
And this would also ease cross language module/library inclusion. Imagine instantiating some Python class from Perl!
Apache would essentially have a mod_parrot. Maybe, if this can be tested very hard, we'd even have a Parrot kernel module for all Unices supporting that. Then exec() could perform compiled scripts right away, like machine code :-)
coralament / best Grötens / liebe Grüße / best regards / elkorajn salutojn
Daniel Pfeiffer
Availability of all the pieces your vision requires is at
least several years away, barring miracles. I don't par-
ticularly expect it to happen.
--
Cameron Laird <Cam...@Lairds.com>
Business: http://www.Phaseit.net
Personal: http://phaseit.net/claird/home.html
Particularly myself; in fact, it was named Parrot precisely to represent its
language-agnosticism.
The thought is also behind why I wrote the Python::Bytecode module, why I
learnt Ruby, and why I've been asking interesting questions about access to
the Ruby AST.
I don't think the reality is as far away as you imply.
--
The problem with big-fish-little-pond situations is that you
have to put up with all these fscking minnows everywhere.
-- Rich Lafferty
Simon certainly has worked with this far more than I.
Maybe it's not dead; it may have just ceased to be.
I had similar problems, since my next stop after the "Parrot tour",
where all this boring VM stuff was decided, happened to be New York
City, home of the extremely famous tic-tac-toe chicken.
(http://members.tripod.com/the-wire/wire0102/ny0102.html)
Since then, of course, the bird has been rescued, given a new home,
and is now much happier.
Whether we need to give a new home to our brand new Virtual Machine
performing bird is not clear, but I'm happily convinced that its soul
will not die off, and that people will make the most of its talents in
the most humane way possible.
--
"(Because if life's not shit, then you're not doing it right.)"
- Rebecca Luckraft
I'd think it quite plausible that if someone took the project on,
there could readily be a pretty functional Python compiler for Parrot
/before/ a Perl 6 one would be likely to emerge...
--
(reverse (concatenate 'string "ac.notelrac.teneerf@" "454aa"))
http://www3.sympatico.ca/cbbrowne/sap.html
Signs of a Klingon Programmer - 7. "Klingon function calls do not have
'parameters' - they have 'arguments' -- and they ALWAYS WIN THEM."
Only just this morning? I take it that parrot itself only came to your
attention recently, since that concept has been with the VM since it's
inception.
> Then no matter whether running a Perl 6, Python, Ruby, Tcl maybe even
> bash script the same interpreter library would be used. Then likely
> it would already be in memory speeding up start time.
The difficult task is compiling those languages into parrot bytecode.
This may not be horrible if those languages have their own bytecode,
which can then be translated into parrot bytecode. As an example, there
already exists a perl script which translates java .class files into
parrot bytecode.
For other languages, a compiler would actually be needed -- eg, for
bash.
> And this would also ease cross language module/library inclusion.
> Imagine instantiating some Python class from Perl!
Ya mean the same way that you can, using Jython, instantiate Python
classes from Java and vice-versa? :)
> Apache would essentially have a mod_parrot.
Well, obviously it would.
> Maybe, if this can be tested very hard, we'd even have a Parrot kernel
> module for all Unices supporting that. Then exec() could perform
> compiled scripts right away, like machine code :-)
Or, your whatever_2_parrot compiler insert an appropriate #! line at the
beginning, pointing to the parrot VM -- this is much simpler, as it
takes advantage of an existing mechanism.
--
my $n = 2; print +(split //, 'e,4c3H r ktulrnsJ2tPaeh'
."\n1oa! er")[map $n = ($n * 24 + 30) % 31, (42) x 26]
Just my £0.02
--
FM
How cool would it be to just set up a computer with a listening parrotd
and DHCP client, plug it in to the Beowulf and have it automagically
recognised as a node and delegated tasks?
I have been thinking quite a bit about cross-platform Beowulfing
recently. My initial idea was to set up an rlogin or something on the
node machines, and have the Apple (that's what I call the conducting
machine: "Apple" because it sends Jobs away, does nothing for a while
then gets Jobs back :-) distribute the source code for the node service
and job processor, configure and build the code, then log out and use
the new node as normal. This would be faster (provided the building
didn't fall over) as Beowulfs go, but less secure and probably harder to
set up.
Comments [rational variety preferred]?
--
FM
Security is a *hard* problem.
Curses! You stole my idea :-) I see what you mean about security being
a problem...
--
FM
What advantage would this have over putting a #! line in the bytecode?
Most existing *nix kernels (except some very old ones) will look at the
#!, parse out the path to an executable program, and the run it.
> I may be wrong. This parrotd system would also help in projects like
> Beowulfs (although why you'd be using byte-compiled languages in
> parallel computing is beyond me), because you could use inetd or
> similar to spawn a parrot when data was received on a certain port.
I don't really see the point of having a parrotd, except if you want to
create something vaguely like PersistantPerl.
--
FM
But not quite more secure. Imagine userX running a script that does
something nasty - like replace some command and then wait for root to
call it. This was a very simple example, since probably parrot would
recreate interpreters per script, but I guess it still could be possible
since it would be the same process...
I guess it's a lot safer to just fork as it is now.
--
WK (written at Stardate 56849.3)
"Data typing is an illusion. Everything is a sequence of bytes."
-Todd Coram
It can't. Parrot can only understand parrot bytecode.
> Why not leave it running, so that any Perl/Python/Tcl/Ruby/Java/sh/
> BASIC/whatever code can get to it immediately?
Ignoring the problem of compiling those languages down to parrot
bytecode...
How should the kernel detect that a parrot-bytecode file is, in fact,
parrot bytecode?
For normal exe files, there's the execute bit and some standard headers.
For normal scripts, there's the execute bit and the #! line.
> A parrotd exec would consist of: "Run this." "OK".
> A #! consists of "Does this file exist?" "Yes." "Does it
> work?" "Yes." "OK, run this." "OK".
> I contend that a properly set up parrotd *should* be faster than the
> equivalent "each language set up separately" environment.
The speed hit of exec() is *not* the time it takes to check if the file
named on the #! line exists and has the executable bit set.... the speed
it is due to the time it takes to load that program into memory.
With that in mind, if you can special-case the kernel to pass scripts on
to special daemons, rather than loading up an interpreter for each
script, it will almost surely be faster, and not just for perl/parrot.
Some years back, RMS proposed a standard 'scripting' language called
Guile. See, various languages like Tcl, Perl, Python, etc. were
being developed and none of them were 1) developed under the FSF nor
2) a variation of Scheme. (A simplified and perhaps biased
interpretation of mine.)
A key part of his proposal was that Guile would support translators
from other languages, like Python, C, etc., to Guile.
It didn't happen. Writing those translators are hard because each
of the languages has different object models, which must be implemented
nearly perfectly. Guile is a full-fledge language built on years of
research into Lisp and scheme, so I would be surprised if it was any
easier to implement in Parrot's byte code. It didn't happen with
Guile, it's even less likely to happen with Parrot.
I can "imagine if parrot ..." But it isn't going to happen without
*huge* amounts of work; comparable to writing Jython. (A Python
implementation written on top of Java rather than C.)
On the other hand, consider the gcc suite of tools. These convert C,
C++, Pascal, Fortran, Objective C, ... into a more generic form. So
there is some known success in doing this. But I've looked at gcc
code and it isn't something I want to hack, while I've contributed code
to Python's C implementation.
Andrew
da...@dalkescientific.com
> Frodo Morris wrote:
> >
> > Benjamin Goldberg wrote:
> > > Frodo Morris wrote:
[...]
>
> > Why not leave it running, so that any Perl/Python/Tcl/Ruby/Java/sh/
> > BASIC/whatever code can get to it immediately?
>
> Ignoring the problem of compiling those languages down to parrot
> bytecode...
>
> How should the kernel detect that a parrot-bytecode file is, in fact,
> parrot bytecode?
>
> For normal exe files, there's the execute bit and some standard headers.
> For normal scripts, there's the execute bit and the #! line.
At least on *BSD, /sys/kern/imgact_*.c control which executable types
are allowed. There are imgact_aout.c, imgact_elf.c, imgact_gzip.c and
imgact_shell.c, and maybe one could add an imgact_parrot.c?
Regards,
Slaven
--
Slaven Rezic - slaven...@berlin.de
Berlin Perl Mongers - http://berliner.pm.org
Isn't it a commonplace that the n x m translation problem is best solved
by having a common intermediate? This seems more efficient when
m+n < n*m
it seems that only those with sufficient muscle can force this on the
world eg Sun/IBM/M$. They can clearly see the need to support only one
backend.
Funnily enough they kept telling me that as soon as I switched to
F99/C++ etc etc that everything would be portable. Now the buzz is
C#/Mono or Java, and perhaps Parrot. As long as the wheels keep turning
and the $'s flow they'll keep promising the next sliced bread.
--
Robin Becker
...each. ;-)
Well, I suppose Sun and IBM are sort of sharing.
Paul
P.S. Projects like Parrot only really tend to succeed if they either
produce results quickly enough for casually interested parties or have
enough direction and motivation for the prolonged period of time that
they aren't producing highly usable output. Despite hearing about
Parrot occasionally, I'm not so sure that Parrot exhibits either of
these properties, but it would be nice to be proved wrong.
I would have to think for a bit to come up with a proof, but
it seems fairly likely to me that that would be impossible.
At the very least, for it to be possible, you would have to
program indefinite-length pointers and allow all available disk
space to be used as extended memory.
The problem that people keep forgetting is that Turing Equivilence
requires indefinite memory. Turing Machines are defined as having
infinite "tapes". Given any finite memory size, I can come up with
an interpreter the emulation of which would not fit in that finite
memory size.
--
Would you buy a used bit from this man??
Your confusion is in thinking that "portable" is a binary value. Fortran
was certainly more portable than assembler, as was C. Tcl is certainly
more portable than Fortran or C.
The other problem, of course, is that people keep improving their
capabilities, so what was portable is no longer adequate. Fortran is
quite portable, as long as you don't want to do 3D graphics driven by a
data glove.
As long as you don't want to do development in multiple languages or
dynamically load code safely over the net into some other OS process,
Tcl and Python are pretty good choices. Otherwise, you might want to
consider C# or Java.
See how it works? :-)
English was portable until everyone started using it ;)
--
Robin Becker
--
FM
There's also the problem of motivation: who's going to write those
translators?
Tcl and Python programmers? They have nicely usable
implementations already. Translation to Guile would buy them
nothing, except for having to rewrite every single C extension.
Guile programmers? If they're Guile programmers, they must like
programming in Scheme. Why would they work on translators for languages
they'll never use?
A Guile programmer might write a Python translator out of pure
bloody-minded evangelism, of course, but it would require supernatural
determination to continue *maintaining* it for the long-term, and
there would still be the risk that the outside community would look at
the translator and ignore it, saying (it's too slow | it doesn't run
extension X that I really need | Guile doesn't run on my platform).
Similar arguments can applied to translators to Parrot, of course.
--amk (www.amk.ca)
ABBESS: In food, in sport, and life-preserving rest
To be disturbed would mad man or beast.
-- _The Comedy of Errors_, V, i
XML, but it wasn't a programming language. And they all kicked to
death because XML parsing is now considered a MUST for any "sane"
language.
--
Giuseppe "Oblomov" Bilotta
"Da grande lotterò per la pace"
"A me me la compra il mio babbo"
(Altan)
("When I grow up, I will fight for peace"
"I'll have my daddy buy it for me")
(snip some)
> that's what I call the conducting
> machine: "Apple" because it sends Jobs away, does nothing for a while
> then gets Jobs back :-)
lol :-))))
(snip some more)
Laotseu
But you were around for this, weren't you, Darren?
Indeed. I was one of the first users, I suspect, compiling my own
safe-tcl interpreter. I have no idea what I was thinking when I included
Tcl in that list. Some double-negation turning into a single-negation
twixt brain and fingers, I think.
--
FM
Each script would need to be done in a seperate safe interpreter which
goes away at the end of the script, so there was no cross process
pollination.
--
Tcl - The glue of a new generation. <URL: http://wiki.tcl.tk/ >
Even if explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.
<URL: mailto:lvi...@yahoo.com > <URL: http://www.purl.org/NET/lvirden/ >
Perhaps a parallel example are the Java implementations of Python and
Tcl - these were written by Java programmers interested in having the
facilities of python / tcl in their java applications.
Perhaps Guile programmers never saw a need for the facilities of perl/tcl/
python.
As long as your application is elegant, ergonometrically superior, and
technologically sexy, it will be an applicable name...
Is it just me, or does anyone else think it peculiar that someone
'@esperanto.org' is championing a unified approach to language?
Daniel, is this part of a scheme to bring Python, Perl, TCL, Ruby
programmers around to your way of thinking?
;)
a
> Is it just me, or does anyone else think it peculiar that someone
> '@esperanto.org' is championing a unified approach to language?
Now, now :-). Esperanto is an auxiliary language, so its intend is to
be a second language that everyone learns in addition to their primary
tongue, rather than a replacement for it.
--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE
/ \ The conviction of wisdom is the plague of man.
\__/ Montaigne
Church / http://www.alcyone.com/pyos/church/
A lambda calculus explorer in Python.
Hmmm, according to OED, "bast" means "sanctuary, refuge, asylum".
:Imagine if parrot could understand all currently- and popularly-
:implemented interpreted languages.
Such as Forth, Word Perfect Macros, Microsoft Visual Basic,
and Befunge?? (You haven't been very specific on the meaning of
'popularly'.)
Now, Parrot is intended for interpreted languages, right? And
the fundamental feature of such languages is that you should
be able to compute a string and then execute the string as
a statement. So Parrot will have to know how to execute statements
in arbitrary source languages. This means that it isn't enough
to have a (say) bash-to-parrot translator that translates
the program and hands it off to parrotd: the entire translator
is going to have to be input along with the translated source.
This indirectly raises an interesting point. If parrotd is glued into
the operating system as part of exec() as has been proposed (as opposed
to the counter proposal to just liet #! do its standard job) then the
implication is that there is only one parrot implimentation per system.
How would one then test new versions of Parrot? With #! it's easy: you
just name the path to the alternative interpreter and the kernel
invokes it. But if parrotd is built right into exec() then in order to
support multiple versions of Parrot, you would have to build the new
versions -in- Parrot and pass them in as yet another layer of
interpreter. e.g., parrotd version 6.302 is running and it has been
passed a copy of the bash2parrot 4.19 translator in case it needs to
interpret a computed string that invokes a program that needs to layer
on the experimental parrotd 6.4.9 interpreter that has to be passed the
perl2parrot translator in case it needs to interpret a computed string
that invokes a program that needs to have -another copy- of parrotd
6.302 layered on [you can't count on parrotd 6.4.9 running parrotd
6.302 programs the same way that parrotd 6.302 would run them] that has
the <etc.> interpeter layered on to run <etc.>.
Scheme is right at home at dealing with these kinds of layering issues,
using a process called "reflection" [well developed by Brian Reid and
Jeem Desriviers]. They aren't straight-forward issues: think about
debugging, and think about re-entrancy, and think about problems in
cross-layer pollution -- you don't want a wild pointer in the fifth
layer down to affect your third layer, right?
Speaking of layers: I take it that some thought has been put into
how to do operating-system escapes? So that one can (for example)
handle graphics? Or even just sockets? Seeing as some of the most popular
interpreted languages are by no means restricted to text-only
and have networking capabilities? But of course the appropriate calls
could come within one of the computed strings, so any escape cannot
be absolute and must instead be handled via the interpretation layer
(c.f. the above in which I showed that parrotd must be built in
Parrot if you want parrotd to be handled at the exec() level...)
--
"The human genome is powerless in the face of chocolate."
-- Dr. Adam Drewnowski
Is it (currently) possible to put Word Perfect macros into a file, and
run that file as a script?
And I think that it's actually highly probable that a translator from
MSVB to Parrot will be written... or rather, a translator from MSIL or
from .NET to Parrot.
> Now, Parrot is intended for interpreted languages, right? And
> the fundamental feature of such languages is that you should
> be able to compute a string and then execute the string as
> a statement. So Parrot will have to know how to execute statements
> in arbitrary source languages.
Well, not parrot *itself* ... you'll have a compiler (written in parrot)
which translates the other language to parrot bytecode. When some piece
of code does eval $string, it calls to the translator, then runs the
resultant bytecode.
> This means that it isn't enough to have a (say) bash-to-parrot
> translator that translates the program and hands it off to parrotd:
> the entire translator is going to have to be input along with the
> translated source.
Fortuantly, parrot is able to defer loading of modules until they're
needed. You hand off the translated source, and as soon as it runs into
an eval $string operation, it loads up the translator, then runs the
translator and continues on it's merry way.
> This indirectly raises an interesting point. If parrotd is glued into
> the operating system as part of exec() as has been proposed (as
> opposed to the counter proposal to just let #! do its standard job)
> then the implication is that there is only one parrot implimentation
> per system.
Yes, this does sound like what was being proposed.
However, nothing prevents you from having more than one parrot kernel
plugin, the selection of which would be distinguished by a version
number in the header of the bytecode file.
> How would one then test new versions of Parrot?
Well, instead of typing in "bytecodeprogram" at the commandline, you
would have to type in "parrot bytecodeprogram", where "parrot" is your
path to your newer version of parrot.
Or perhaps you would do:
parrot_daemon --nokernel &
parrot_send_to_daemon bytecodeprogram
(or something like that)
Obviously this won't be *quite* the same environment as the kernel's
plugged-in parrot interpreter daemon, but it will be sufficiently close
for *most* forms of testing.
> With #! it's easy: you just name the path to the alternative
> interpreter and the kernel invokes it. But if parrotd is built right
> into exec() then in order to support multiple versions of Parrot, you
> would have to build the new versions -in- Parrot and pass them in as
> yet another layer of interpreter.
I don't understand what you mean by this.
> e.g., parrotd version 6.302 is running and it has been passed a copy
> of the bash2parrot 4.19 translator
Err, no. You would have a program which was originally written in bash,
then translated into parrot bytecode. All places where strings need to
be translated at runtime (via the eval command) are set up to load up
the bash translator, then translate that string, then run the string.
The bash2parrot translator would not be *included* in the translated
bytecode, though -- only the name of the module, or maybe the filename
of the module. Vaguely like how one shared object can depend on another
one, or even whole reams of other ones -- but they don't get loaded
until functions in them get called.
> in case it needs to interpret a computed string that invokes a program
Ok, at this point, stop, and take a breath. When your translated bash
program wants to "invoke a program", *how* that program's guts work is
beyond it's concern -- it merely calls fork and exec and wait.
The fact that the exec() may send the filename and args and environment
to a different parrot daemon is irrelevant to that process.
> that needs to layer on the experimental parrotd 6.4.9 interpreter that
What do you mean 'layer on' ?
> has to be passed the perl2parrot translator in case it needs to
Again, nothing is passing around translators -- they only get loaded
when they are needed.
> interpret a computed string that invokes a program
> that needs to have -another copy- of parrotd 6.302 layered on [you
> can't count on parrotd 6.4.9 running parrotd 6.302 programs the same
> way that parrotd 6.302 would run them] that has the <etc.> interpeter
> layered on to run <etc.>.
Your confusion seems to mainly stem from three things: You're not
paying attention to process boundaries, and you think that every
translated program in language X has the translator for X to parrot
embedded into it, and exec() doesn't invoke a sub-process, it replaces
the current process with a new process (That's why you have to fork()
first if you want a sub-process).
Once you have a better understanding of the unix process model, the
problems you've sketched will disappear.
> Scheme is right at home at dealing with these kinds of layering
> issues, using a process called "reflection" [well developed by Brian
> Reid and Jeem Desriviers]. They aren't straight-forward issues: think
> about debugging, and think about re-entrancy, and think about problems
> in cross-layer pollution -- you don't want a wild pointer in the fifth
> layer down to affect your third layer, right?
Fortuantly for us, seperate processes have their own memory address
spaces, so this isn't a concern.
> Speaking of layers: I take it that some thought has been put into
> how to do operating-system escapes? So that one can (for example)
> handle graphics? Or even just sockets? Seeing as some of the most
> popular interpreted languages are by no means restricted to text-only
> and have networking capabilities?
Remember, the parrot interpreter is written in C... it has to be.
Obviously, one would have parrot opcodes which result in calls to C
functions, which perform socket operations or graphics or whatever.
> But of course the appropriate calls could come within one of the
> computed strings, so any escape cannot be absolute and must instead be
> handled via the interpretation layer (c.f. the above in which I showed
> that parrotd must be built in Parrot if you want parrotd to be handled
> at the exec() level...)
You can't write parrotd in parrot -- you have to write it in something
which runs on the hardware (elf or a.out).
Writing parrotd in parrot is similar to writing a turing machine VM in
the language you use to write descriptions of turing machines, and then
having exec() detect when you're running a compiled turing machine and
handing it off to the turing machine VM ... if it doesn't get passed off
to *something* running on the actual hardware, it's going to loop
forever.
> Walter Roberson wrote:
> >
[...]
> > Speaking of layers: I take it that some thought has been put into
> > how to do operating-system escapes? So that one can (for example)
> > handle graphics? Or even just sockets? Seeing as some of the most
> > popular interpreted languages are by no means restricted to text-only
> > and have networking capabilities?
>
> Remember, the parrot interpreter is written in C... it has to be.
> Obviously, one would have parrot opcodes which result in calls to C
> functions, which perform socket operations or graphics or whatever.
>
I dont't think it will be opcodes, but rather a combination of loadlib
(load a DLL) and dlfunc (create a parrot-callable subroutine from a
DLL function). See core.ops.
Regards,
Slaven
--
Slaven Rezic - slaven...@berlin.de
Visualize Makefiles with GraphViz:
http://user.cs.tu-berlin.de/~eserte/src/perl/GraphViz-Makefile/
Ok, so loadlib is one op, and dlfunc is another op :)
One particular point of possible interest: there
was, quite predictably a Guile Tk (guile-tk) <URL:
http://wiki.tcl.tk/GuileTk >.
[lots of technical stuff]
Ack. My brain hurts. Make it stop....
--
David K. Wall - use...@dwall.fastmail.fm
/me recalls the captured prisoners in Quake2
And Guile programmrs is the right choice of words. Most Scheme
programmers does not use Guile. In most cases there are better
alternatives such as DrScheme and Bigloo.
A common bytecode that Perl, Python, Ruby and Tcl would all compile to? Hmm.
Scary, and for several reasons. Firstly, I'm not at all convinced that the
object models are compatible enough. And secondly, how are you going to avoid
requiring a resident interpreter/compiler to handle the case where you've got
dynamic code coming from a source that doesn't generate parrot bytecodes
directly (e.g. a normal programmer^Wperson!) After that, there's worrying about
security models to do as well. ;^)
One way forward, at least with Tcl, would be to implement a Tcl interpreter in
terms of parrot bytecodes (rather like Jacl is a Tcl interp written in Java) but
that tends to bring out questions like "why bother?" Could it be done without
requiring a chunky auxiliary C library to provide little things like
cross-platform select() support? This sort of thing tends to make me suspicious
that this is little more than a pipe-dream, well, at least as far as Tcl's
concerned. (I don't know the other languages nearly well enough to comment in a
useful way.)
Alternatively, formulate Parrot as a sort-of disjoint union of the bytecodes
used in each language, so unifying them without unifying anything. >:^D
Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ donal....@man.ac.uk
-- OK, there is the MFC, but it only makes the chaos object orientated.
-- Thomas Nellessen <nell...@gmx.de>
[Scary ... worrying ... security ... suspicious ... pipe-dream]
Brazil (1985).
--
Joe Schaefer "The eternal mystery of the world is its comprehensibility."
--Albert Einstein
If you were writing a VM for one language, and then tried to extend it
for the others, then I would agree wholeheartedly. However, it's no
more strange than using MSIL for C++ and VB and other languages.
> And secondly, how are you going to avoid requiring a resident
> interpreter/compiler to handle the case where you've got
> dynamic code coming from a source that doesn't generate parrot
> bytecodes directly (e.g. a normal programmer^Wperson!)
In all cases that I can think of, your dynamic code is essentially a
string which gets eval()ed (with a language-dependent eval). The
solution is to make the eval() function/operator compile into a sequence
of operations along the lines of: Load the compiler for this language,
(if it's not already loaded), pass that string to the compiler, run the
generated bytecode.
> After that, there's worrying about security models to do as well. ;^)
>
> One way forward, at least with Tcl, would be to implement a Tcl
> interpreter in terms of parrot bytecodes (rather like Jacl is a Tcl
> interp written in Java)
Considering that Jcl and Jython exist, it seems like a reasonable goal
would be to make an interpreter which turns Java's .class files into
Parrot .pasm files. Once that tool exists, one could simply translate
Jcl and Jython into parrot... there would be no need to re-implement
them.
And one day, in the distant future, there will be a Perl6 decompiler,
which will turn Parrot bytecode into Perl6. Then we'll be able to
convert the translated Jython and Jcl into Perl6 :)
> but that tends to bring out questions like "why bother?" Could it be
> done without requiring a chunky auxiliary C library to provide little
> things like cross-platform select() support?
No matter *what* language you're using, some sort of interface to select
or poll will be needed, and that interface would be implemented in C.
Whether it's "chunky" or not depends on how that interface appears on
the high-level end of things.
> This sort of thing tends to make me suspicious that this is little
> more than a pipe-dream, well, at least as far as Tcl's concerned. (I
> don't know the other languages nearly well enough to comment in a
> useful way.)
>
> Alternatively, formulate Parrot as a sort-of disjoint union of the
> bytecodes used in each language, so unifying them without unifying
> anything. >:^D
The set of bytecodes which Parrot will be using has been decided, and
while it's not fixed in stone, it's quite unlikely to get a whole other
language's bytecodes added to it.
Assuming you thouroughly understand Tcl's bytecodes, why not take a look
at Parrot, and see whether the set of bytecodes that parrot supports is
sufficient to do everything that Tcl's bytecodes do?
Seems like an awful lot of overhead for every keystroke, window event,
and async file operation.
Why would any of these require that strings be eval()ed?
You compile the string to bytecode, *once*, and pass this compiled code
as the callback for your keystrokes, window events, and async file
operations. You wouldn't pass a string to be eval()ed -- that would be
silly.
Furthermore, even if one did do something that foolish, the compiler
needs to be loaded only once... So, (ignoring the first one, where the
compiler gets loaded) each keystroke, window event, or async file
operation would merely compile the string to bytecode, then run the
bytecode. This is no different from what Tcl does all the time, except
that it's a different kind of bytecode.
Bindings substitute their values. File events get additional arguments.
Etc.
> Furthermore, even if one did do something that foolish, the compiler
> needs to be loaded only once...
I missed your parenthetical comment on first reading. Yes, sorry.
So? Pass these in as arguments. There's no need to recompile a
procedure for each and every different set of arguments that might be
passed to it. That would defeat the point of having procedures in the
first place.
> > Furthermore, even if one did do something that foolish, the compiler
> > needs to be loaded only once...
>
> I missed your parenthetical comment on first reading. Yes, sorry.
Peculiar -- if I'd been reading someone else's description of it, and he
didn't say, "(if it's not already loaded)", I would have *assumed* that
it wouldn't be loaded if it already had been, *unless* he said something
to the contrary (like, "yes, I really do mean reload the compiler from
disk each and every time we eval() a string")
A binding isn't a procedure. Indeed, I'm pretty sure that [eval] doesn't
cache bytecode in bindings because it's more inefficient than
regenerating the bytecodes each time. It's good style to invoke a
procedure, but hardly necessary. And indeed, putting something like
"break" or "continue" inside a procedure called by a binding doesn't
have the same effect as putting it in the binding.
That depends on your interface to Tk ... in perl, a binding *is* a
procedure. Well, actually it's either a procedure or a method name.
bind $b '<Button>', sub { print "Button pressed.\n" };
The 'sub' keyword creates an unnamed procedure.
> Indeed, I'm pretty sure that [eval] doesn't
> cache bytecode in bindings because it's more inefficient than
> regenerating the bytecodes each time. It's good style to invoke a
> procedure, but hardly necessary. And indeed, putting something like
> "break" or "continue" inside a procedure called by a binding doesn't
> have the same effect as putting it in the binding.
What are the semantics of "break" and "continue" in a procedure, when
they don't refer to a label or loop within that procedure?
They return with a different exit code, I believe. In other words, it
depends on the caller.
Security and Microsoft outlook/Internet Explorer - past 5 years or more...
(JCL is something else. I'd rather not remember it thankyouverymuch.)
> would be to make an interpreter which turns Java's .class files into
> Parrot .pasm files. Once that tool exists, one could simply translate
> Jcl and Jython into parrot... there would be no need to re-implement
> them.
>
> And one day, in the distant future, there will be a Perl6 decompiler,
> which will turn Parrot bytecode into Perl6. Then we'll be able to
> convert the translated Jython and Jcl into Perl6 :)
$10 says that only ever happens with a performance hit. The problem is that not
all bytecodes are created equal. (And Jacl is an implementation of Tcl in Java
more in the way that the usual form of Tcl is an implementation of the language
in C. The fact that Java uses bytecodes is pretty much just a distraction
here. We also have another way of integrating Tcl with Java that keeps Tcl
implemented in C, but which integrates almost identically with the Java
language.)
> > This sort of thing tends to make me suspicious that this is little
> > more than a pipe-dream, well, at least as far as Tcl's concerned. (I
> > don't know the other languages nearly well enough to comment in a
> > useful way.)
[...]
> Assuming you thouroughly understand Tcl's bytecodes, why not take a look
> at Parrot, and see whether the set of bytecodes that parrot supports is
> sufficient to do everything that Tcl's bytecodes do?
I know a bit about Tcl bytecodes, and a key factor about them is that they are
very tightly targetted towards implementing Tcl.
Hmm. A quick scan through the documentation doesn't really raise my hopes. Or
even leave me with a deep enough understanding of what's going on; is there any
deeper description than http://www.parrotcode.org/docs/parrot_assembly.pod.html
about? (OK, Tcl's bytecodes need documentation too, but I've already gone to
the effort to understand those as part of my maintenance and development
duties. I've just not got enough hours in the day.) Unfortunately, the bits
that I'm most interested in seem to be the bits with least info (isn't that
always the way with complex software systems?)
First impressions: what is meant by "string" anyway? Character sequence? Byte
sequence? UTF-8 sequence? ISO 8859-1 sequence? [FX: Reads docs] Oh, they
carry about what their encoding is with them? That must make working with them
fun. How does it handle things like the blecherous monstrosities[*] used for
system encodings in the Far East? On a quite separate point, is there a
strncmp() equivalent? That would make implementing Tcl much easier...
More generally, Tcl would need to use PMCs throughout. The problem is that
Tcl's value semantics (copy-on-write) do not line up well with that which Parrot
seems to use (object-based) and which, IIRC from the discussions at the time
when Parrot was being created, are closely based on those used in Perl even if
not precisely coincident. Hence we'd be unable to use ground values except as
part of the implementation of higher-level concepts. That'll impact badly on
performance.
It's at this point that I feel a round of "Stuff it. I'll stick to implementing
in C." coming on. I've been quietly watching Parrot for a while now, and I
still don't think that implementing Tcl in it is really a winning proposition.
I'd love someone to prove me wrong, but proof is building a Tcl interpreter in
or on top of Parrot and running the Tcl test suite on it (and getting a decent
proportion of the tests passing.)
BTW, how does Parrot handle calls to foreign code? The docs I've seen are on
the hazy side, and integration with existing C, C++ and FORTRAN monoliths is
(alas) all too important in particularly commercial development.
Donal.
[* The cluster of things known as "Shift-JIS" justifies this term. IMHO. ]
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ donal....@man.ac.uk
-- The small advantage of not having California being part of my country would
be overweighed by having California as a heavily-armed rabid weasel on our
borders. -- David Parsons <o r c @ p e l l . p o r t l a n d . o r . u s>
OK, let's go for a more concrete question. What sequence of bytecodes would let
me open a socket and talk to a remote host? The Parrot I/O operation opcodes
don't mention anything about sockets and nor can I tell how I would invoke a
piece of helper C or C++ to do the job on my behalf.
Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ donal....@man.ac.uk
<snip interesting discussion, which mostly goes over my head :>
>
> BTW, how does Parrot handle calls to foreign code? The docs I've seen are on
> the hazy side, and integration with existing C, C++ and FORTRAN monoliths is
> (alas) all too important in particularly commercial development.
>
On this particular point; has anyone thought of writing a unified C-API for Tcl,
Perl, Python, Ruby, (Java) etc? What I mean by this is that each of these
languages can be extended in C, and quite often are. Also, whenever I see
interesting new C/C++ libraries implemented, I also tend to see seperate
language bindings for each of these languages. This seems like a massive
duplication of effort. Would it be possible to abstract over the C API's of
these languages to a common core of functionality which all share (e.g. getting
an interpreter handle, registering a command, setting a variable etc)? I have
only dealt with Tcl's C interface, so this may be too difficult, but it seems
like it would be a huge step forward.
In terms of problems implementing this, I can see several problems:
1. Method of calling C-coded procedures. In Tcl, arguments are passed to C
procedures as Tcl_Obj's. Obviously this would be different for Perl or Python.
Is it possible to come up with an API which can convert to the appropriate
object type, or to some intermediate type?
2. Accessing functions which are not present in all languages. I'm sure there
are API's in Tcl which only make sense with respect to Tcl, and probably the
same in the other languages. So, how would one create a general API which
allows you to call language specific APIs? In the same way one handles platform
specific APIs?
3. Versioning. Which language versions are compatible with which abstract API? I
can see this one becoming insane over time. However, there are people with much
more experience with these issues than me, so there might be a way.
4... probably more, this is all coming of the top of my head.
Hmm.. the more I think about this, the more problems it seems to present. I'd
love to be able to write an extension, and have it instantly work with x
different langauges. Also, I'd love to be able to use Python and Perl
extensions from Tcl, without loading seperate interpreters and all that. Am I
dreaming of an impossible Utopia?
Neil "soo naive" Madden
--
package r Tkhtml;package r http;pack [scrollbar .v -o v -co {.h yv}] -s right\
-f y;pack [html .h -ys {.v set}] -f both -e 1;bind .h.x <1> {eval g [.h href %x\
%y]};proc g u {set t [http::geturl $u];.h cl;.h p [http::data $t];http::cleanup\
$t;.h co -base $u};g http://mini.net/tcl/976.html;proc bgerror args {};# NEM :-)
Erm... that's the old IBM Job Control Language? You mean this one?
http://www.tuxedo.org/~esr/jargon/html/entry/JCL.html
Bleh, forget I mentioned it. :) Twas a horrible typo :)
> > would be to make an interpreter which turns Java's .class files into
> > Parrot .pasm files. Once that tool exists, one could simply
> > translate Jcl and Jython into parrot... there would be no need to
> > re-implement them.
> >
> > And one day, in the distant future, there will be a Perl6
> > decompiler, which will turn Parrot bytecode into Perl6. Then we'll
> > be able to convert the translated Jython and Jcl into Perl6 :)
>
> $10 says that only ever happens with a performance hit. The problem
> is that not all bytecodes are created equal. (And Jacl is an
> implementation of Tcl in Java more in the way that the usual form of
> Tcl is an implementation of the language in C.
So Jacl still converts Tcl into, well, Tcl bytecodes, even though it's
doing so in Java? Blech.
Hmm, is there a way of making tcl dump the tcl-bytecodes to a file?
If so, one could probably make an attempt to translate those bytecodes
into parrot. (And ignore Jacl).
Having read http://www.parrotcode.org/docs/strings.pod.html only just
now myself, it's possible I could be wrong on this, but...
Each string's encoding can be one of native, utf8, utf16, utf32, or
foreign. So those "blecherous monstrosities" will either be converted
to one of the utf formats, or else have their own string vtable.
For now, they will probably be converted... the strings.pod.html says
this at the bottom:
Foreign Encodings
Fill this in later; if anyone wants to implement
new encodings at this stage they must be mad."
> On a quite separate point, is there a strncmp() equivalent? That
> would make implementing Tcl much easier...
You mean, for testing the first n characters of two strings for
equality? There isn't that I know of, but one could always be added;
furthermore, it supposedly will be possible to make lightweight strings
which are substrings of other strings, without any copying involved.
You could make your strncmp be a wrapper around making a substring of
the first n characters of each of your two strings, and comparing those
substrings.
> More generally, Tcl would need to use PMCs throughout.
Why? (Not an objection, but I don't know much about Tcl's bytecode)
> The problem is that Tcl's value semantics (copy-on-write) do not line
> up well with that which Parrot seems to use (object-based)
Parrot will do copy-on-write.
Furthermore, Parrot may implement some strings as ropes, so that the
amount that needs to be copied will be even smaller.
> and which, IIRC from the discussions at the time when Parrot was being
> created, are closely based on those used in Perl even if not precisely
> coincident.
Perl is likely never going to implement strings as ropes. It does now
have copy-on-write, though this is a recent development.
Perl5.6+ has two internal encodings for strings -- bytes and utf8.
Parrot not only allows native, utf8, utf16, and utf32, but it also
allows any kind of user-defined encoding one might want. I doubt that
perl5 will ever do this.
> Hence we'd be unable to use ground values except as part of the
> implementation of higher-level concepts. That'll impact badly on
> performance.
>
> It's at this point that I feel a round of "Stuff it. I'll stick to
> implementing in C." coming on. I've been quietly watching Parrot for
> a while now, and I still don't think that implementing Tcl in it is
> really a winning proposition.
> I'd love someone to prove me wrong, but proof is building a Tcl
> interpreter in or on top of Parrot and running the Tcl test suite on
> it (and getting a decent proportion of the tests passing.)
Parrot does everything in two steps -- compile, then run. Most likely,
it will have a compiler which converts Tcl bytecode to Parrot bytecode.
Whether or not Parrot will ever translate from Tcl source to Parrot
bytecode is another question entirely.
Thinking a bit more, particularly about how Tcl often needs to interpret
strings at runtime, I realize that no non-trivial Tcl program can work
without having a string-to-bytecode compiler. Needless to say, this
poses a problem.
> BTW, how does Parrot handle calls to foreign code? The docs I've seen
> are on the hazy side, and integration with existing C, C++ and FORTRAN
> monoliths is (alas) all too important in particularly commercial
> development.
Although I don't know *how* it will handle foreign code, I do know that
it *will* handle foreign code, and have a better interface than Perl5's
cruddy XS extension language.
> Donal K. Fellows wrote:
>
> <snip interesting discussion, which mostly goes over my head :>
>
> >
> > BTW, how does Parrot handle calls to foreign code? The docs I've seen are on
> > the hazy side, and integration with existing C, C++ and FORTRAN monoliths is
> > (alas) all too important in particularly commercial development.
> >
>
> On this particular point; has anyone thought of writing a unified C-API for Tcl,
> Perl, Python, Ruby, (Java) etc? What I mean by this is that each of these
> languages can be extended in C, and quite often are. Also, whenever I see
> interesting new C/C++ libraries implemented, I also tend to see seperate
> language bindings for each of these languages. This seems like a massive
> duplication of effort. Would it be possible to abstract over the C API's of
> these languages to a common core of functionality which all share (e.g. getting
> an interpreter handle, registering a command, setting a variable etc)? I have
> only dealt with Tcl's C interface, so this may be too difficult, but it seems
> like it would be a huge step forward.
Look at
http://www.swig.org/
Regards,
Slaven
--
Slaven Rezic - slaven...@berlin.de
Tired of using file selectors? Real programmers use the TAB key for
completion and not for jumping around. Try
http://search.cpan.org/search?mode=module&query=Tk::PathEntry
Just as there are "stdio", "unix", and "win32" ParrotIO layers, one
would define a "socket" layer (and maybe an "xti" layer, for the really
adventurous). Obviously, this needs a bit more C code to be added.
Once that layer is added, you could read from and write to sockets just
like you would from files.
"Neil Madden" <nem...@cs.nott.ac.uk> wrote in message
news:LRaA9.1325$J55.2...@newsfep2-win.server.ntli.net...
> Hmm.. the more I think about this, the more problems it seems to present.
I'd
> love to be able to write an extension, and have it instantly work with x
> different langauges. Also, I'd love to be able to use Python and Perl
> extensions from Tcl, without loading seperate interpreters and all that.
Am I
> dreaming of an impossible Utopia?
I wrote some DLL helper logic that makes it easy to query a vtable by name
in another DLL.
The vtables can be created in whatever way, but the vtable is not assocated
with allocated memory, as is the case with COM. This makes it quite flexible
without really being limited because one could decide that the first
function should return a 'this' pointer and the second function should be
the destructor, or whatever.
In any DLL that can link statically with a piece of C, you can use this
framework to pass functions around. (Even if you can only link dynamically,
you could have a helper dll to expose vtables). I use some #define macros to
create the vtables statically (see snippet below). It's then very easy to
take arbitrary C functions and wrap them up in one or more vtables. However,
vtables need not be statically allocated (and this is significantly
different from public dll functions).
Because the vtables are looked up by name initially, you could handle
versioning like in COM: "mycomponent.vtablename.1", where
"mycomponent.vtablename" would refer to the most recent version - but it is
preferable to always specificy an exact version.
Contrary to COM this is completely cross platform as there is no OS magic
involved.
When a vtable is created, it is helpful to write a C prototype struct that
matches, because this makes things easier clientside - but it is not
required (see snippet below).
On the clientside I wrote some small wrappers to ease loading dll's
dynamically and in C++ to wrap the optional prototype. I only did this for
Windows, but the principles are essentially the same on Linux, just like
Ruby loads .so files.
The framework is generic - here is what I did to access functionality in a
OCaml parser application I wrapped up as selfcontained dll - without using
public dll functions except for init, query vtable and uninit functions: I
picked all the most relevant OCaml API logic for allocating memory on the
OCaml runtime stack and created a set of vtables approximately one for each
.h file in the API. The Ocaml OCaml functions I wrote (parse_file) I had to
dynamically call a function to locate the OCaml function address. In the dll
init logic I performed this operation and added the result to a vtable
already prepared for the purpse. The client of the dll loads the dll, calls
init and then queries relevant vtables, but in principle has no clue that it
really is OCaml the executes the logic (in principle because in praxis I
wanted to know in order to efficiently allocate memory).
The same thing could be done in Ruby, wrapping rb_... functions into vtables
and having separate vtables for calling Ruby code - this vtables would be
created dynamically or at least filled dynamically.
Any language that supports C integration and dynamic link libraries can use
this framework.
Incidentally Parrot works a lot with vtables, so there could be some overlap
here (I didn't know that at the time I wrote the framework though).
I have not put the code online, but if anyone is interested, let my know.
Below is a readme snippet, a bit technical and not the only way to use the
framework (it would be possible to map Ruby functions into dynamically
created vtables for instance).
Mikkel
<snip>
Interfaces are organized in vtable maps which are statically allocated
arrays of VtblMapEntries.
VTBL_MAP_BEGIN(<vtbl_map_name>)
VTBL_MAP_ENTRY(name, vtbl)
... more entries here ...
VTBL_MAP_END
The name <vtbl_map_name> is later used be the linker, such that the map
can be hooked into a master
map of all vtable interfaces compiled together.
The master map is located in a central compilation unit such that new
vtable maps can easily be
added to the master map without modifying the source of any of the
existing map providers.
Before entering the map into the mastermap, it must be declared - unless
the map is earlier in the same
compilation unit as the master map:
VTBL_DECLARE_MAP(<vtbl_map_name>)
... more maps declared here ...
Following the map declareations, there is a master map which is scanned
by the default lookup function:
VTBL_MASTER_MAP_BEGIN(<vtbl_master_map_name>)
VTBL_MASTER_MAP_ENTRY(<vtbl_map_name>)
... more master map entries here ...
VTBL_MASTER_MAP_END
Typically, a dynamically loaded library (dll) will have a purpose
specific mastermap using a selection
of available vtbl interface maps.
Example:
we have the following functions in a .c file. Moreover, we have the
malloc and free functions
from the std library. We want all four functions wrapped in two
interfaces: FooBar and Mem.
First we create a header file for the interfaces:
<file "examples.h">
struct
{
int (*get)(int x);
void (*set)(int x, int val);
void *(*create)(int x);
} ExamplesVectorVtbl;
struct
{
void *(*allocate)(size_t size);
void (*deallocate)(void *p);
} ExamplesMemoryVtbl;
</file>
<file "examples.c">
#include <memory.h>
#include "vtbl.h"
#include "examples.h"
void set(void *p, int x, int val) { return ((int*)p)[x] = val; };
int get(void *p, int x) { return ((int*)p)[x]; }
void *create(int x) { return calloc(x * sizeof(int)); };
ExamplesVectorVtbl { get, set, create } vector_vtbl;
/* shows that existing library functions can be packaged as well */
ExamplesMemoryVtbl { calloc, free } mem_vtbl;
VTBL_MAP_BEGIN(examples_vtbl_map)
VTBL_MAP_ENTRY("Examples.Vector", vector_vtbl)
VTBL_MAP_ENTRY("Examples.Memory", mem_vtbl)
VTBL_MAP_END
</file>
<file "master.c">
/* to get vtbl.h and the lookup function in vtbl.c */
#include "vtbl.c"
VTBL_DECLARE_MAP(examples_vtbl_map)
VTBL_MASTER_MAP_BEGIN(vtbl_master_map)
VTBL_MASTER_MAP_ENTRY(examples_vtbl_map)
VTBL_MASTER_MAP_END
void *GetNamedInterface(char *name)
{
return vtbl_master_map_lookup(vtbl_master_map, name);
}
</file>
<file "client.c">
#include "examples.h"
void *GetNamedInterface(char *name);
void test()
{
/* since interfaces are static, pMem and pVec need not be
deallocated */
Examples_Memory *pMem =
(Examples_Memory*)GetNamedInterface("Examples.Memory");
Examples_Vector *pVec =
(Examples_Vector*)GetNamedInterface("Examples.Vector");
void *v1 = pMem->allocate(sizeof(int[4]));
void *v2 = pVec->create(4);
pVec->set(v1, 2, 42);
pVec->set(v2, 0, pVec->get(v1, 2));
pMem->deallocate(v1);
pMem->deallocate(v2);
}
</file>
Typically the client would have loaded a dynamically linked library and
found the address of the published
GetNamedInterface function. Once that function is avaible, it is easy to
access all the remaining functions
via the named interfaces.
</snip>
Basically the same as the Tcl STUBS mechanism for loading extensions in
different versions of the tcl interpreter without recompiling or
relinking the extension.
Michael
I hope I'm wrong about this, that there's an easier way,
and that someone will correct me.
On the other hand, at the level of application development,
working programmers should *not* be doing much of the [eval]
kind of string interpretation that once was thought necessary
style in Tcl, as well as Lisp and very few other languages.
Source code should look straightforward and plenty
procedural, and, in general, will not "interpret strings"
after a first round of bytecode compilation.
OK, what sequence of bytecodes would instantiate and invoke those layers? The
expositions I've found online so far have been rather too dry for me to actually
see how such a thing could be done.
Donal (fed up of hand-waving, particularly in his day job. Must write code...)
--
"Windows is a car with square wheels (architecture) and a huge engine (hype,
etc.), capable of of making the car move despite the square wheels. Linux
is a car with round wheels but a small engine, capable of making the car go
despite the small engine." -- John Latham <j...@cs.man.ac.uk>
Umm, err, I don't know... I've merely looked (briefly) at the docs and
source of Parrot, and never programmed for it. But <handwave>I'm sure
that it *can* be done</handwave>.
> Donal (fed up of hand-waving, particularly in his day job. Must write
> code...)
Sorry about the handwaving, but it's the best I can offer. If it can't
be done now, someone will add it in the future.
> "Windows is a car with square wheels (architecture) and a huge engine
> (hype, etc.), capable of of making the car move despite the square
> wheels. Linux is a car with round wheels but a small engine, capable
> of making the car go despite the small engine."
> -- John Latham <j...@cs.man.ac.uk>
I would say that Linux has an equally big engine, but a manual
transmission, which discourages folks who are used to automatic.
I'm *fairly* sure it can be done. I was just trying to evaluate
whether it could be done with a reasonably small input of effort or
whether it was going to be a really big porting job. (Everything I've
seen so far makes me think that it will be enough effort that I'll
leave it to someone with a commercial interest in having things work
together.)
>> Donal (fed up of hand-waving, particularly in his day job. Must
write
>> code...)
>
> Sorry about the handwaving, but it's the best I can offer. If it can't
> be done now, someone will add it in the future.
It's just that given the size of the semantic disconnect between Tcl
and Parrot, it's pretty clear to me that there's going to be great
amounts of C code (or some other language, I suppose) involved in
building a bridge between the two. Hence my intense interest in
seeing how this might be done, and my irritation at someone saying
"oh, it must be possible". And I was feeling frustrated at cow-orkers
that day too... ;^)
Donal (posting through Google due to sucky local news-server.)
--
[This space unintentionally left blank.]
> That depends on your interface to Tk ... in perl, a binding *is* a
> procedure.
In Tcl it isn't. It's a string.
--
I've seen things you people can't imagine. Chimneysweeps on fire over the roofs
of London. I've watched kite-strings glitter in the sun at Hyde Park Gate. All
these things will be lost in time, like chalk-paintings in the rain. `-_-'
Time for your nap. | Peter da Silva | Har du kramat din varg, idag? 'U`