I do use Tcl, and so far I have not found anything lacking. I am only
doing small admin scripts so I am sure in the greater world of Tcl
there is still a lot for me to learn.
So what exactly does Tcl lack? I am not talking "OO" stuff, because I
can take or leave that. Fundamentally in the Tcl language itself is
more of what I am asking about.
So, if you answer this, please let me know if 8.5 or the 9 wishlist
would fix what you currently consider "wrong" with Tcl as that would be
good to know for myself.
I am just trying to get a feel for what you guys the Tcl community
think about the language as a whole.
Thanks,
Robert
s> In reading some of the wiki pages like "Who says Tcl sucks"
s> (http://wiki.tcl.tk/640), bring to my mind just what does Tcl lack
s> exactly? 8.5 is around the corner and there is a 9 wishlist. I am not a
s> "programmer" so I don't know what someone would consider missing to
s> make Tcl an all around great language to use.
s>
s> I do use Tcl, and so far I have not found anything lacking. I am only
s> doing small admin scripts so I am sure in the greater world of Tcl
s> there is still a lot for me to learn.
s>
s> So what exactly does Tcl lack? I am not talking "OO" stuff, because I
s> can take or leave that. Fundamentally in the Tcl language itself is
s> more of what I am asking about.
s>
s> So, if you answer this, please let me know if 8.5 or the 9 wishlist
s> would fix what you currently consider "wrong" with Tcl as that would be
s> good to know for myself.
s>
s> I am just trying to get a feel for what you guys the Tcl community
s> think about the language as a whole.
Tcl lacks Perl's excessively obscure operator collection.
Tcl lacks Forth's postfix notation.
Tcl lacks C's optimized compilation.
Tcl lacks COBOL's verbosity.
Tcl lacks FORTRAN's rigid format.
Hmm. Tcl does not really 'lack' anything really important in a *functional*
sense. It only lacks certain 'theological' features...
s>
s> Thanks,
s>
s> Robert
s>
s>
\/
Robert Heller ||InterNet: hel...@cs.umass.edu
http://vis-www.cs.umass.edu/~heller || hel...@deepsoft.com
http://www.deepsoft.com /\FidoNet: 1:321/153
> So what exactly does Tcl lack? I am not talking "OO" stuff, because
> I can take or leave that. Fundamentally in the Tcl language itself
> is more of what I am asking about.
The language in and of itself is fantastic. Having the syntax that it
does makes it extremely malleable - the fact that something like 15
years after being created, it's still working great is a testament to
that.
--
David N. Welton
- http://www.dedasys.com/davidw/
Apache, Linux, Tcl Consulting
- http://www.dedasys.com/
> Tcl lacks FORTRAN's rigid format.
Since the Fortran 90 standard, Fortran has supported a free format as
well as the rigid fixed format (for backwards compatibility).
[...]
> So what exactly does Tcl lack? I am not talking "OO" stuff, because
> I can take or leave that. Fundamentally in the Tcl language itself
> is more of what I am asking about.
Closures, mostly. And if it had closures, then lambda. And
presumably proper lexical scoping would then make sense. That's not
in 8.5, and I'd guess the changes that seem to me to be necessary
would be too radical for closures ever to get into Tcl, so I'm not
holding my breath for them to appear in 9.
[...]
> Starting with 8.5 (and the new {expand}-feature coming with it)
What's {expand} feature? Can you explain me, or where can I read about
it?
--
Pozdrawiam (Greetings)!
Googie
[...]
> So what exactly does Tcl lack? I am not talking "OO" stuff, because I
> can take or leave that.
It is already available and there are at least three libraries
realizing that. The power of Tcl is that it was able to have it as
"just a library", as probably the only programming language in the
world.
> Fundamentally in the Tcl language itself is
> more of what I am asking about.
>
> So, if you answer this, please let me know if 8.5 or the 9 wishlist
> would fix what you currently consider "wrong" with Tcl as that would
be
> good to know for myself.
Hmm... I think, there would be nice some support for functional
programming (I mean some good lambda - local function, with attached
environment - and recursively programmed algorithms, even with
different syntax). Another thing is some exceptional flow control,
allowing to break more than one loop, for example.
It is hard to find anything that lacks in Tcl, because most of the
things you need you can program with commands and {}. If it is
impossible in Tcl itself, it is possible in attached dynamic library.
> I am just trying to get a feel for what you guys the Tcl community
> think about the language as a whole.
:)
The currernt definitive reference is probably:
http://www.tcl.tk/man/tcl8.5/TclCmd/Tcl.htm
To read about the background to it, search through the archives of c.l.t
and tcl-core; enormous amounts of time and effort were devoted to the
discussion, and what's there is very much a syntactic compromise...
Donal.
> what does Tcl lack exactly?
Many people say: "Central package archive", like CPAN in Perl.
With CPAN you can easly and fast download needed packages to run Perl
script on any machine connected to internet.
--
Pozdrawiam (Greetings)!
Googie
Well, in a *functional* sense you could make a case for things like
lambdas (anonymous procedures). That's the one feature I'd really love
to see go into Tcl more than anything else. Interestingly, Tcl is one of
the few[*] languages where you can create your own lambda, as the
numerous examples on the wiki demonstrate. But a fast, byte-code
compiled version with proper cleanup in the core would be nice. There
are 3 TIPs, that I am aware of, that propose adding anonymous procedures
(187, 194, and 196 (withdrawn)). There doesn't seem to have been any
activity on these for a long time though, and I fear that they are
stalled indefinitely.
Other cool features to have would be closures (although the close
relationship to objects probably makes these contentious too), and
continuations (all kinds of fun, but not a serious suggestion as I don't
think they could possibly work with Tcl's current design, particularly
interaction with the C stack).
[*] I don't think I've seen an example in any other language. The other
languages I can think of where this might be possible (Lisps mainly)
already have some form of lambda.
Linguistically, Tcl (in 8.5) doesn't lack much at all. Or rather what it
lacks (mutable opaque first-class values, and all the stuff that flows
from that[*]) it lacks intentionally.
The major things left are stuff like providing standard OO, better
exception handling, a standardized package repository, a grand whizz at
marketing, etc. They're not core language issues (the marketing bit
isn't a language issue at all) but they'll make a massive difference if
they're addressed. I don't know how many can be addressed in 8.5, but
all can be dealt with then in principle.
Donal.
[* It's actually a major restriction and a major strength. ]
Starting with 8.5 (and the new {expand}-feature coming with it), I'd
consider the language itself (at long last!) complete.
There is always lots of potential in new commands and improving
existing ones.
binary scan & exec immediately spring to mind, more
will likely do as soon as I'm finished posting this.
There are also things that already exist as a fundamental
part of tcl but still aren't clearly documented: it's the
rules for parsing a list from a string, which are similar
and related but not equal to the rules of script-parsing.
I am not expert enough in Tcl but I would gladly donate my time to help
and test (and I would learn Tcl more to boot).
Robert
FORTH and Lisp both handle this aspect nicely, too. Not as nicely as
Tcl, mind... :-)
As for what it lacks, some mechanism to automatically dispose of values
when references to them go away. When the last "file6" goes away, file 6
should get closed. When nobody needs the command $myobject, that command
should get cleaned up. Admittedly, very difficult to do, to the point
where I can't even imagine how to begin thinking about how to do it.
Perhaps some specialized allocation mechanism that you could
occasionally garbage-collect programatically.
--
Darren New / San Diego, CA, USA (PST)
He had a name like someone sneezing with
a mouth full of alphabet soup...
One of the most important changes Tcl needs:
[dict] + drop away Tcl arrays and use $x(foo) syntax as
syntax sugar for [dict].
This is how code looks after the change:
proc make-point {x y} {
set p(x) $x
set p(y) $y
return $p
}
proc add-points {p1 p2} {
make-point \
[expr {$p1(x)+$p2(x)}] \
[expr {$p1(y)+$p2(y)}]
}
proc draw-point p {
commandToDraw $p(x) $p(y)
}
set a [make-point 10 20]
set b [make-point 1 2]
set c [add-points $a $b]
draw-point $c
Programs written this way are more modular, layered, and clean.
The following is an interactive session with a Tcl interpreter
having this feature already working, just to show the feeling
of having dict support inside the syntax:
Welcome to Jim version 0, Copyright (c) 2005 Salvatore Sanfilippo
0 jim> set a(foo) bar
bar
0 jim> set a(Tcl) Cool
Cool
0 jim> puts $a
foo bar Tcl Cool
1 jim> puts $a(Tcl)
Cool
0 jim> unset a(foo)
0 jim> puts $a
Tcl Cool
People that already played with dicts may wonder what happens
if a given variable is not a valid dictionary:
0 jim> set a "x y z"
x y z
0 jim> $a(hello)
Runtime error, file "?", line 1:
Variable 'a' does not contain a valid dictionary
I think that [dict]s are a major positive change to Tcl ability
to solve problems in a clean way, and to the scalability
of the language itself. This is already done in 8.5 and
that's cool. To integrate it *really* in the language
as a drop & replacement of arrays is another very important
point to make the [dict] semantic "cheap" for the programmer.
Of course there are many other important things Tcl may get.
More support for functional programming like a [lmap] command,
garbage colleced / compiled [lambda]s, a standard OOP system
and so on. But this are outside the language semantic itself,
while the change I (and many others) proposed is not.
Ciao,
Salvatore
Have you worked out how this would have to interact with the other
[dict] operations, variable traces, and so on?
Other dict operations will play well, the system just adds
a syntactically way to perform a subset of the [dict] operations.
Instead traces are source of incompatibility, because now it is possible
to set traces on single array elements, while it is not possible
with dict elements, being a dict a single entity, and a Tcl array
instead a "collection" of variables.
This single reason of backward compatibility (support for traces
on array elements) is the biggest pratical problem AFAIK, and what
maybe stopped the inclusion of this feature in Tcl 8.5?
I think traces are nice but I may hardly compared the usefulness of
arrays as first class values with the ability to set traces on array
elements.
Of course it is still possible to set traces to the dict as a whole.
Ciao,
Salvatore
Traces on array-elements are definitely a useful feature, just
like traces on variables. It (e.g.) allows array-elements to
be used as -textvariables to some widgets.
> Of course it is still possible to set traces to the dict as a whole.
not exactly.
You can set a trace on a *variable* that may contain a dict.
Dicts are "immutable", and as such cannot be traced, because
they never change. it's only the variables that can be overwritten
with a new dict-value, and in some cases, namely if refCount is 1,
then the old value can be directly reused, which is the reason
for constructions like set a [linsert $a 0 x y z][unset a]
That said, I don't understand, what would be so bad about that:
% set a(1) 42 ;# makes "a" an array
% puts $a ;# auto-converts to dict, (then furtheron to string).
1 42
% entry .e1 -textvar a(1) ;# that's ok
.e1
% set b $a ;# b is a scalar variable holding a dict-version
# of array "a"
% puts $b(1) ;# index-operation on dict-value
42
% entry .e2 -textvar b(1) ;# nope,nope,nope
.e2
(doesn't complain, but also has no connection to anything, until
scalar variable b is unset, just like it is now, if b already
exists as a scalar)
Yeah, useful, but dicts as replacement of arrays turn Tcl in a
totally new thing from some POV. What do you prefer?
Array traces are mostly used in Tk contexts, and most of the times
in contexts where to write procedures to get/set state may
replicate the exact functionality in a less obfuscated way.
Instead I think Tk programming is one context where the ability
to take complex state via dicts (possibly nested) can be a big
win. If Tk programming relies on the ability to set traces on
array elements, there is Tk to fix...
>>Of course it is still possible to set traces to the dict as a whole.
>
> not exactly.
> You can set a trace on a *variable* that may contain a dict.
Sure, as usually. No one can set traces against Tcl_Objects
because traces are a feature of variables.
I mean: you can set traces on variables containing dicts.
> Dicts are "immutable", and as such cannot be traced, because
> they never change. it's only the variables that can be overwritten
> with a new dict-value, and in some cases, namely if refCount is 1,
> then the old value can be directly reused, which is the reason
> for constructions like set a [linsert $a 0 x y z][unset a]
>
> That said, I don't understand, what would be so bad about that:
> % set a(1) 42 ;# makes "a" an array
> % puts $a ;# auto-converts to dict, (then furtheron to string).
This does not make it a first-class objects, and of course it is
not acceptable performace-wise to continuously convert dicts into
arrays, because what happens once you pass again $a and then the
receiver does set b(2) 55? Also, the trace is gone, once you convert
it to string, every var-specific info is lost.
what about: set a [foobar $a]?
And of course, no nesting. This is just the current situation with
some sugar instead of [array get].
> 1 42
> % entry .e1 -textvar a(1) ;# that's ok
What you really want here IMHO, is to have a callback
and the code organized in a different way.
-textvar is a facility, can't justify a broken design
in a fundamental part of the language.
Nor to have [set a(foo) $newval] to automatically update an entry in
a Tk widget, or similar things.
In the second case, the clean alternative is:
setAndUpdateProc a foo $newval
Of course all this in my opinion.
Ciao,
Salvatore
That is one of the differences between FORTRAN as many people knew
it at one time and still think it is, and Fortran, as many other people
use it now.
Regards,
Arjen
This breaks the "Model/View/Controller" design, which you should look up.
In other words, this requires the code that calculates $newval to
understand how many widgets are looking at that value. You can have
multiple traces on the same variable, and that's very handy.
In other words, imagine a long-running process that simply calls
[incr percentage]
one hundred times during its execution. Now you want to display a
progress report. Without variable traces, you would need to modify your
long-running code to know you're displaying a status bar, or maybe it's
a dialog box with a bar in it. Or a dialog box with a bit of text in it.
None of which should matter to the long-running calculation.
Now what might be a good idea is to add the syntactic sugar for
referencing dict values, but not get rid of arrays.
> antirez wrote:
> > In the second case, the clean alternative is:
> > setAndUpdateProc a foo $newval
> This breaks the "Model/View/Controller" design, which you should
> look up.
Other languages manage MVC just fine without resorting to traces...
> Other languages manage MVC just fine without resorting to traces...
Sure, but you (a) have to implement it yourself, and (b) breaks cdoe
that is already implementing it with trace. I.e., it's not a minor thing
to break trace, but a fairly big design mechanism that would need to get
replaced in every program that currently uses it. Not something that's
easy to search-and-replace, in other words.
> David N. Welton wrote:
> >>This breaks the "Model/View/Controller" design, which you should
> >>look up.
>
> > Other languages manage MVC just fine without resorting to traces...
>
> Sure, but you (a) have to implement it yourself, and (b) breaks cdoe
> that is already implementing it with trace. I.e., it's not a minor
> thing to break trace, but a fairly big design mechanism that would
> need to get replaced in every program that currently uses it. Not
> something that's easy to search-and-replace, in other words.
That's certainly true, unfortunately. Unless you could make
-textvariable and the like automatically set up the new mechanism
internally, but I'm not sure how that might work...
> So what exactly does Tcl lack?
The language itself does not lack much. But there are important things
around Tcl, which it lacks:
- more spread and marketing, wide usage by more people
- a really good documentation tool for Itcl and Tcl. I know, there are
many, but I haven't found one that works really good. The tool I am
searching for reads Itcl Class files and creates HTML in the style of
the java API-docs generated by javadoc.
- Fixing of all the broken links in the internet, especially on
www.tcl.tk (I mean the "Resource center under repair" thing). But not
only on this site - there are many broken links on other sites, too.
But apart from that I don't see lacks...
Eckhard ;)
Ok, for the record, could someone explain closures? They seem to
come up as a topic of discussion about once every 2-3 months, and
for the past few times I've been scurrying off to the wiki for an
explanation. The wiki page begins with words to the effect that
someone needs to define them, then launches off into what seems to
be the transcript of a chat session which assumes that everyone
reading is intimately familiar with the concept. From what I can
comprehend on that page, "closures" might as well be just a
shibboleth used by an in crowd to differentiate themselves the rest
of us. If it is to be anything more, someone needs to clearly
define it and provide examples to elucidate the concept and show its
usefulness. Who knows, you might even convince someone sitting on
the sidelines to say to themselves "neat concept, don't know how I
live without it" and proceed to TIP up a sample implementation.
--
Rich Wurth / rwu...@att.net / Rumson, NJ USA
Does http://en.wikipedia.org/wiki/Closure_%28computer_science%29 help?
> I am just trying to get a feel for what you guys the Tcl community
> think about the language as a whole.
From _Existentialism and Humanism_ by Jean-Paul Sartre (excerpts from
which can be found at
<http://www.cis.vt.edu/modernworld/d/Sartre.html>):
But if you seek counsel--from a priest, for example--you have
selected that priest; and at bottom you already knew, more or
less, what he would advise. In other words, to choose an
adviser is nevertheless to commit oneself by that choice. If
you are a Christian, you will say, consult a priest; but there
are collaborationists, priests who are resisters and priests
who wait for the tide to turn: which will you choose? Had
this young man chosen a priest of the resistance, or one of
the collaboration, he would have decided beforehand the kind
of advice he was to receive. Similarly, in coming to me, he
knew what advice I should give him, and I had but one reply to
make.
It would work a lot like [trace] does, I would think. ;-)
> Ok, for the record, could someone explain closures?
I'm not sure to what wiki you've been scurrying but have you tried
reading something like <http://c2.com/cgi/wiki?LexicalClosure> or
<http://www.perl.com/doc/FAQs/FAQ/oldfaq-html/Q3.14.html>? Does this
help?
> > So what exactly does Tcl lack?
> - a really good documentation tool for Itcl and Tcl. I know, there are
> many, but I haven't found one that works really good. The tool I am
> searching for reads Itcl Class files and creates HTML in the style of
> the java API-docs generated by javadoc.
Said this, one thing regarding Tcl's lexical features comes into my
mind. I remember when I was using some Python once, that there are
"documentation strings". These are strings escaped with 3 quotes
"""doc...""" and are usualy written directly below function and class
declarations. A tool in the python core distribution (pydoc) parses
these strings and generates the documentation in HTML. (Well, it is
not very well used sometimes but that's a different story).
That is a feature I would like to see in Tcl/Itcl.
Eckhard ;)
I like Lisp but I cannot value implementations of 10MB image, lacking
simple graphics or ignoring OS existence. Also Python sometimes seems
to be too big for me.
If not big enough, Tcl should be standard part
of every Linux distribution: as is bash, ls etc.
Also - in old, good days Linux kernel could be configured with Tk
script, in newer versions they switched to Gtk and it makes only more
problems...
But this is only IMHO.
Regards
From time to time there is this discussion about adding closures to
Tcl, but usually the idea is to make they similar to languages like
Scheme, i.e. closures with lexical scoping. My opinion is that this is
very hard to do with Tcl, and even not in the spirit of the language.
In a Tcl program, what part of a procedure body is a variable is not
defined until execution, for example:
set a 10
closure {x} {
incr a
}
Then I define 'incr' to be like 'puts', and 'a' is no longer a variable.
It was already suggested that in Tcl closures should not have any kind
of creation-time resolution rule, but that the context where they are
defined should be captured as a whole, and used when the closure is
running to resolve "unbound symbols" (that are better referred as
variables and procedures not otherwise defined during execution in
Tcl slang). Still there is the problem that to similuate the lexical
scoping you have to take *references* to the shared environment, so that:
set a 10
set foo [closure {x} {
incr x
}]
set bar [closure {x} {
incr x
}]
$foo ;# -> 11
$bar ;# -> 12
foo and bar will share the same 'a'.
I think this is a all to complex and UnTclish, so I tried to design a
new semantic for closures.
The semantic is based on a single command [closure], that can set/get
closure variables in the scope of the current procedure, together with
a minimal change in the [proc] and [lambda] command (I know we don't
have lambda... but still there is a way Tcl users already think about
it and should change).
Bisically, to set a closure variable, there is to write the following
command:
closure set x 10
this will set 'x' inside the closure of the current procedure. This 'x'
will be persistent accross different calls of this procedure, and all
this environment will be destroied once the procedure itself is
destroied. The procedure can test for the existence of a closure
variable with [closure exists x], can get the value with [closure get x]
and so on.
The change required to the [proc] command, is that it can take an
optional further argument, that's a Tcl list of key/value pairs used
to inizialize the closure at procedure creation time.
so:
proc foo {} { .... } {x 10 y 20}
will create the procedure foo with x=10 and y=20 inside the closure.
The same for lambda.
The following is an example of procedure that returns a progressive
integer number every time it's called:
proc counter {} {
if {![closure exists x]} {
set x 0
}
set x [closure get x]
closure set x [expr $x+1]
return $x
}
Of course it's better to use the optional argument
of proc and write it as:
proc counter {} {
set x [closure get x]
closure set x [expr $x+1]
return $x
} {x 0}
Of course for this to be very useful, lambda it's needed.
This version of lambda, like proc, should accept the optional
argument to initialize the closure. This is an example:
proc createAdder {x} {
lamba {y} {expr $y+[closure get $x]} [list x $x]
}
set f [createAdder 5]
$f 10 ;# -> 15
I think that this design can do everything lexical scoping is able to
do, but with a command-based interface that plays better with Tcl.
This kind of closures will be added into the Jim interpreter, a
small footprint Tcl interpreter I'm writing. This interpreter is already
working and I'll be happy to send a preview tar.gz to interested
people (the license is the APACHE2, so it's possible to use Jim in
commercial projects if needed).
Regards,
Salvatore
> Also - in old, good days Linux kernel could be configured with Tk
> script, in newer versions they switched to Gtk and it makes only
> more problems...
I think that there should be more promotion of good GUIs with Tk.
Tk makes GUI creation very easy, but creating a high quality GUI is
tough.
One of the things that have made Gnome and KDE such a success is that
they promote usability standards, showing people the way to make
usable programs.
Some may say they aren't there 100% yet, but the important thing is
that there are guidelines for refining and improving their work.
I forgot to include a couple of points.
The closure command can also access closures of external procedures:
[closure in $procName get/set x ...] and so on.
This makes possible to share the environment if really needed.
Example:
proc createAdder {x} {
set a [lamba {y} {expr $y+[closure get $x]} [list x $x]]
set b [lamba {incr} {closure in $a set x $incr} [list a $a]]
list $a $b
}
this returns one closure to add and one to change the increment
performed by the first closure.
Also it is worth to note that this way to do closures don't play
well with functions as values nor with lambas based on auto expansion,
because full closures need to be able to modify their environment
so they can not be immutable values.
Ciao,
Salvatore
There is a typo here,
the code is instead:
> set a 10
> set foo [closure {x} {
> incr a $x
> }]
> set bar [closure {x} {
> incr a $x
> }]
> $foo 1 ;# -> 11
> $bar 1 ;# -> 12
sorry for the error.
Ciao,
Salvatore
In this newsgroup, I assumed most would interpret "the wiki" to be
shorthand for the Tcl'ers wiki. I was looking at:
<http://wiki.tcl.tk/closures>, a page that is opaque to someone not
already familiar with closures.
Thanks for the pointer to other sources.
proc myproc {} {
local x 0
incr x
return $x
}
The [local x 0] would declare a proc-local variable "x", which would be
initialized to 0 on the first run through the command.
Rob Seeger
Excactly! Make this "local" (a bad name IMHO ... maybe 'static' is
more appropriate, at least C programmers will have some idea about
the meaning) accessible from outside, and at creation time, and you
have the same powerful as my proposal.
It's very important to be able to inizialize at procedure-creation time
because to inizialize inside the body with a non-constant value means
to use [format] or other tricks to create the body at runtime.
In your example, what about if you want to return a lambda where 'x'
is initialized to some value that's not constant?
You have to write something like
proc y {increment} {
lambda {} [format {
local x %s
incr x
return $x
} $increment]
}
If you can set the closure at creation time with an additional argument
and/or via a command to access the closure, you solved this problem
with the following code:
proc y {increment} {
lambda {} {
local x
incr x
return $x
} [list x $increment]
}
To fully mimic scheme-alike closures is also very important to be
able to access a procedure closure from another one.
But the important point here is: yes, your [local] command
creates a closure. Still I like more to have closures handled
using a command, this makes always clear inside a given procedure
if $foo is a random variable, or if there is an access to a closure
element. Also in my implementation/design closures are not really
variables, so they are not subject to traces and so on, but they
are a [dict] associated to a procedure.
Regards,
Salvatore
I'd also like to point out that much of what I've seen stated as the
benefits of closures are already amazingly easy to do in Tcl. I don't
see a real need for them. I like the idea of procedure-local variables,
but I can already see problems with implementing them, especially in
conjunction with lambdas (where they'd be particularly useful).
For example, where is the data for a lambda stored? How is that data
known to exist when you access the lambda after shimmering? You can't
use the args/body as keys, because then you could only use each lambda
definition once... which is bad.
set lam [lambda {a b} {
local bool 0
if { [incr bool] } {
return -1
} else {
return 1
}
}]
lsort $mylist -command $lam
lsort $mylist2 -command $lam
puts [llength $lam]
lsort $mylist3 -command $lam
What happens when you do this?
Rob Seeger
I have been pondering how to do this. Based on how I would implement
this in the core, I was rather thinking of declaring and/or initialising
the static vars outside the body - for instance, extending the proc
syntax to allow for a fourth argument:
proc argList body ?staticArgList?
Rob's example would look as follows:
proc myproc {} {
incr x
} {{x 0}}
Ie, a proc that has a static var 'x' initialised to '0'. The
staticArgList has the same structure as the usual argList. As seen from
the body, 'x' is just a local variable.
Note that this syntax accomodates array static vars, but *not* giving
them a default initialisation.
Telling you guys this in order to get early criticism, now that the
subject is on the air ;)
Miguel
Personally, I prefer the "in body" approach to declaring local
variables. Part of my preference is "it feels right", and the other part
is the dynamic nature of the whole thing. Much like I tend to write
things like:
proc myproc {a {bName ""}} {
if { [string length $bName] } {
upvar 1 $bName b
}
....
}
I would also like to be able to do the same thing with local vars. I
don't need to decide if I want to use the local var until the proc is
actually running. I have no use case for this desire, it just feels
right to me.
Rob Seeger
when I tried it one year ago, there where lots of dependencies to
satisfy first for installing it. And then, the output is still not as
good as the javadoc generated things.
Ok, it is not too bad, i have to admit - maybe a nice looking gui and
pluggable output filters as well as reduced dependencies would have made
it more comfortable for me.
I will have a closer look at it... and yes, some documentation tool of
this kind should be standard in the Tcl core or at least in tcllib.
Eckhard ;)
If closures worked nicely, it would be even nicer if one could then have
a variant of [vwait] that created a closure for the callback (perhaps
leveraging [trace]?) so one could do something like
proc x {} {
blah blah
vwait one
more blah
vwait one
more blah
}
and step thru the procedure while *not* blocking other vwaits.
% set mylam [lambda {i} { incr n $i } {n 5}]
{apply {i} { incr n $i } {n 5}}
% puts [$mylam 2] ;# This invalidates the string rep for mylam
7
% puts $mylam ;# The string rep, newly generated
{apply {i} { incr n $i } {n 7}}
Rob Seeger
Robert Seeger wrote:
> Miguel,
>
> Personally, I prefer the "in body" approach to declaring local
> ...snip...
#######################################################
# Closures:
proc closure {name parms dict body} {
set nbody ""
foreach {k v} $dict { # better: dict for ...
append nbody "[list set $k $v]\n"
}
append nbody $body
proc $name $parms $nbody
}
closure incrN {varname} [dict create N 42] {
upvar $varname v; incr v $N
}
incrN foo ;# does what "incr foo 42" would have done.
closure select {v} {
(1) {puts first} (5) {error "I don't like 5"}
(42) {return} () {puts "That's nothing"}
} { eval $($v) }
select 42
#################################################
# static variables in procedures:
# the trick define them as global-vars in an
# appropriate procedure-specific namespace.
proc static {var val} {
set proc [lindex [info level 2] 0]
namespace eval "::statics::${proc}" {
if {![info exist $var]} { set $var $val }
}
uplevel [list upvar ::statics::${proc}::$var $var]
}
# NOTE: there are probably some rough edges when using it in
# procedures defined and called from within some namespace,
# and thus not appearing fully qualified in [info level 1]
# For extra soundness, one would have to redefine the command
# "rename" to also rename (or delete) the proper sub-namespace
# of ::statics !
# That's it.
I don't know what is a closure because I never used one.
From the Wiki I guess that it's some anonymous procedures (like [callback]).
I never seen any interest to that.
And I never understoud why Tcl needs closures.
ulis
> Even if Tcl lacks something, more desirable is its simplicity. [...]
>
> I like Lisp but I cannot value implementations of 10MB image,
> lacking simple graphics or ignoring OS existence. [...]
CLISP is a Common Lisp of which I know that is probably as good a
comparison to Tcl as one could find, though there are many Common Lisp
implementations (ECL <http://ecls.sourceforge.net/> might be as good
if not better for this purpose) and this doesn't even consider its
slimer cousin, Scheme. CLISP is a decent comparisoin because unlike
some other Common Lisps which have compilers which can produce
aggresively optimized native machine code
<http://home.comcast.net/~bc19191/blog/040308.html>, CLISP has a byte
code compiler. As a Common Lisp, it is still more feature rich than a
stock Tcl. It includes builtin support for pseudo-unlimited integrer
arithmetic, e.g. computing the factorial of 500 will produce the
correct answer, fractions, lexical closuers, an object system with
builtin support for multiple dispatch with a metaobject protocol,
etc., etc., etc. There are probably Tcl extensions for just about all
of these, though I'd be surprised if Tcl had anything like CLOS and
the MOP. CLISP also has sockets
<http://clisp.cons.org/impnotes/socket.html>; knows about the OS in
terms of the shell, pipes <http://clisp.cons.org/impnotes/shell.html>,
and a foreign function call interface
<http://clisp.cons.org/impnotes/dffi.html> that allows access to the
native OS APIs; there are any number of different graphics libraries
<http://www.cliki.net/graphics%20library> and toolkits
<http://www.cliki.net/Graphics%20Toolkit> available, though not
bundled with CLISP (but even Tcl has its Tk).
Let's look at some numbers. The following is from "top", having just
started the programs and not done anything with them. Now, I'm using
Tcl/Tk on a project, with some extensions we needed built into the
interpreter. "dpwish" is the customized version used on the project.
PID USERNAME THR PRI NICE SIZE RES STATE TIME CPU COMMAND
18870 dkick1 1 48 0 5440K 4120K sleep 0:00 0.00% lisp.run
18868 dkick1 5 58 0 3256K 2568K sleep 0:00 0.00% tclsh8.4
28393 dkick1 5 55 0 6200K 5144K sleep 0:00 0.24% wish8.4
14163 dkick1 1 54 0 4632K 3704K sleep 0:00 0.02% dpwish
Hmmm... looks like wish8.4 has a bigger size and resident in memory,
too. What does it mean? <shrug> But it doesn't look like a
slam-dunk, CLISP is fat-n-bloated while Tcl/Tk is slim-n-lean, to me.
The Tcl/Tk interpreter itself tries to pass itself off as lighter than
it actually is by pushing code into shared libraries, so let's count
those, too, as if we had a self contained, statically linked
executable.
% du -csh .../bin/dpwish .../lib/libtcl8.3.so .../lib/libitcl3.2.so \
> .../lib/libtk8.3.so .../lib/libitk3.2.so .../lib/libtclx8.3.so \
> .../lib/libtkx8.3.so /usr/lib/libsocket.so.1 .../lib/libX11.so.5.0 \
> /usr/lib/libnsl.so.1 /usr/lib/libm.so.1 /usr/lib/libc.so.1 \
> /usr/lib/libdl.so.1 /usr/lib/libmp.so.2
16K .../bin/dpwish
684K .../lib/libtcl8.3.so
144K .../lib/libitcl3.2.so
932K .../lib/libtk8.3.so
56K .../lib/libitk3.2.so
192K .../lib/libtclx8.3.so
12K .../lib/libtkx8.3.so
70K /usr/lib/libsocket.so.1
684K .../lib/libX11.so.5.0
896K /usr/lib/libnsl.so.1
112K /usr/lib/libm.so.1
1.2M /usr/lib/libc.so.1
6.0K /usr/lib/libdl.so.1
25K /usr/lib/libmp.so.2
4.9M total
I'm not an expert on the internals of CLISP (it doesn't just
exclusively use either static or dynamic linking but also code loading
of its own) so I might be missing something but I think I got
everything.
% du -csh .../bin/clisp .../lib/clisp/base/lisp.a \
> .../lib/clisp/base/lisp.run .../lib/clisp/base/lispinit.mem \
> /usr/lib/libc.so.1 /usr/lib/libdl.so.1 \
> /usr/openwin/lib/libXext.so.0 /usr/openwin/lib/libSM.so.6 \
> /usr/openwin/lib/libICE.so.6 /usr/openwin/lib/libX11.so.4 \
> /usr/lib/libnsl.so.1 /usr/lib/libsocket.so.1 /usr/lib/libm.so.1 \
> /usr/lib/libmp.so.2
32K .../bin/clisp
3.5M .../lib/clisp/base/lisp.a
2.5M .../lib/clisp/base/lisp.run
1.1M .../lib/clisp/base/lispinit.mem
1.2M /usr/lib/libc.so.1
6.0K /usr/lib/libdl.so.1
144K /usr/openwin/lib/libXext.so.0
56K /usr/openwin/lib/libSM.so.6
128K /usr/openwin/lib/libICE.so.6
928K /usr/openwin/lib/libX11.so.4
896K /usr/lib/libnsl.so.1
70K /usr/lib/libsocket.so.1
112K /usr/lib/libm.so.1
25K /usr/lib/libmp.so.2
11M total
Rougly twice the size. CLISP clearly takes up more disk space. So if
that's what is most important to you, don't go with CLISP over Tcl/Tk.
<pause> I've spent way too much time on this already and nothing I've
posted is conclusive, even to me myself. Shall we consider CLISP plus
however many extensions to Tcl/Tk with however many extensions? For
example, Common Lisp has bignums by default, which will take some
extra size in the image, but I know that one can access similar
functionality in Tcl with the bignum package <http://wiki.tcl.tk/8608>
and libgmp or even bignum in pure Tcl <http://wiki.tcl.tk/12315>. How
would one compare the size of bignums implemented in pure Tcl with
something like libgmp, unless one could completely ignore all other
aspects, like speed, for example? Or should one just consider that
specified in the language standard; i.e. dismiss anything in CLISP
that is not discussed in the ANSI standard
<http://www.lispworks.com/documentation/HyperSpec/Front/index.htm>,
anything that is an implementation extension? But AFAIK, there is no
ANSI/ISO standard for Tcl. How could one make a fair comparison? Tcl
has a standard implementation while Lisp has any number of different
implementations, both commercial
<http://www.alu.org/table/systems.htm#vendor> and free
<http://www.cliki.net/Common%20Lisp%20implementation>. Did Miroslaw
mean Common Lisp or the Lisp family? Because something like Guile
<http://www.gnu.org/software/guile/guile.html> is the Lisp family
answer to Tcl; i.e. it is meant to be embeddable and Scheme is a far
slimer standard language than Common Lisp. I don't know enough about
ECL to comment on it, though.
Anyway... I think there is a lot of FUD about Lisp. Use Tcl if you
like it but like it for the right reasons. Personally, I think Tcl
shines as an embeddable tool control language <smile> but not
necessarily because it might be "smaller" than some other language but
rather it was designed for this task, so it has many features helpful
towards that end.
And that is the problem: 2/3 of the world cannot reuse your object.
Miguel, after some sleep and pondering about every option, I think your
design is the best we can get. With some minor change is roughly
semantically powerful as lexical scoping, but trivial to understand and use:
(define (foo)
(let ((x 10))
(lambda (y) (+ x y))))
proc foo {} {
set x 10
lambda y {
+ $x $y
} x
}
the 'x' in the lambda is a static var, the change I propose is that the
intialization value for a static is mandatory, if you don't provide it,
the value of the variable with the same name currently defined in the
procedure creation context is used as intialization (anyway a static var
without initialization hardly makes sense).
Another change could be to add a command that's able to
modify from the "outside", the static vars of a procedure.
I think that this changes don't violate the initial semplicity of your
design but may it a bit better when the user is using the feature in
the closure declination. That said I hope to see this in Tcl ASAP
and I think my interpreter will be better served with this design
compared to my original one.
Thanks!
Salvatore
Being Tcl 9.. or even more, there must be a point
where one can break existing scripts without
problems if a change it's worth it.
Of course the change will hardly be worth it from
the point of view of many people that have
working Tk scripts with an interesting design using var
traces on array elements, but this is not always
a good reason for the language to take a misfeature:
to have a fundamental data type that's not first class,
something that traditionally forced Tcl programmers to
find new strange ways to perform incapsulation and
nesting, using with namespaces, or lists where key names
and not indexes were the good solution, and so on.
> Arrays as currently defined in Tcl -- named
> collections of variables, not collections of
> values like dictionaries -- are an incredibly
> powerful building block. I'd be most upset
> if they went away.
If some Tk problem is solved very well with traces
on array elements, maybe there is something to
change in the Tk design so that there is an equivalently
good way to work? Also, isn't it trivial with dicts
to create a couple of procedures that allows to
have traces on dict elements?
Just the exception (Tk programming with array traces) will
be where it's needed to call procedures, and all the rest
will be the case where you can use the syntax sugar $foo(bar).
I mean you can define a procedure [myset dictVarName key value]
and [myget $dictValue key] that test for __read__trace__$key
and __write__trace__$key to check if there is an associated script
and then execute it. Then you have just to avoid to use the syntax
glue, and you have again the same powerful.
I think it's worth to try hard to create a new way to replicate what
it used to be comfortable with array traces on array elements,
but to take the language locked forever just because at every
step there will be N people with M broken scripts if this changes
does not seems wise to me.
Regards,
Salvatore
Parsing this format should not cause any problems, I think.
Schelte.
--
set Reply-To [string map {nospam schelte} $header(From)]
Hello,
this is exactly one of the ideas I had yesterday night, but it
was lost this morning. Thanks!
I agree 100%.
Urgh... that really isn't how the dual-rep works, and it violates Tcl's
value semantics. If you expand the puts [$mylam 2] line, then what you
get is:
puts [{apply {i} { incr n $i } {n 5}} 2]
and what the apply command gets is 4 args (in this case, not in TIP
194): i { incr n $i } {n 5} and 2. What you are suggesting is that the
apply command would *directly alter* the *value* (note: not a variable)
passed as the 3rd argument. That's a whole world of pain right there.
Values are values - they don't change.
First-class (i.e. value) and mutable (i.e. variable) don't mix in Tcl.
If you want to pass something around which is going to be mutated, then
you need to give it a name (i.e. put it in a variable). That way, you
get the necessary level of indirection. So, in your example above we
might do:
set mylam [lambda {i} { incr n $i } myscope]
where myscope is a variable holding a dict, say. Alternatively, the
lambda command could do this for you:
% set mylam [lambda {i} { incr n $i } {n 5}]
{apply {i} { incr n $i } ::lambda::somescope}
where the ::lambda::somescope points to a fresh variable containing the
dictionary. So, we are using a dictionary in a variable as a mutable
container, without ever getting its string rep. May as well just make it
a namespace (and get traces etc). This is pretty much what TIP 194
proposes, IIRC.
-- Neil.
I find it pointless to discuss if you don't appear to
accept the fundamental difference between mutable
and immutable objects (vars and arrays versus Tcl_Objects)
Since arrays are no "data type" in the first place, they
can't be first nor whateverth class at all.
> Also, isn't it trivial with dicts
> to create a couple of procedures that allows to
> have traces on dict elements?
Sure, you can set a write-trace on the variable holding
the dict, and whenever a new value is written to the
variable, you compare the new value with a backup of
the old value, and fire registered callbacks for certain
types of detected changes.
there are some disadvantages to this approach:
1.) it is mindboggingly inefficient
2.) it needs double space (one extra copy to compare
the dict value after changing)
3.) it may destroy objects stored in the dict, because
for comparing untyped values, they need to be converted
to string.
All in all its surely no adequate substitute for arrays.
> Just the exception (Tk programming with array traces) will
> be where it's needed to call procedures, and all the rest
> will be the case where you can use the syntax sugar $foo(bar).
Yes adding the syntax sugar $dict(key) is a "good thing"(tm) :-)
and should not have any problems with array & dicts coexisting,
...except that assigning to "d(key)" would not be allowed for
a skalar variable d holding a dict.
I guess that this was the argument against doing it that
way, but I don't find this very compelling. I'd guess that
the last word about this matter has not been spoken yet.
> I mean you can define a procedure [myset dictVarName key value]
> and [myget $dictValue key] that test for __read__trace__$key
> and __write__trace__$key to check if there is an associated script
> and then execute it.
"set" is some levels too high to really intercept all
changes to a variable. There are other commands that modify
variables, such as lappend, append, incr, ... and thats why
the hooks used by trace are founded a few levels deeper.
I've got the impression you haven't fully understood traces
and now try to reinvent them...
>> Ok, for the record, could someone explain closures?
>
> I don't know what is a closure because I never used one.
Depending on which language you've used, you may never have used a
language which supported closures. For example, Tcl, C, C++, Java
don't. Even in languages which do, it's quite often just as
convenient to use objects.
> From the Wiki I guess that it's some anonymous procedures (like [callback]).
No, it's to do with having code which captures its environment (or
some aspects of its environment, in particular name bindings).
Closures often get used with anonymous procedures (often called
lambdas), but that's not the only place they occur. (And anonymous
procedures can be useful in languages which don't have closures. For
example for most of its history Lisp implementations didn't have
closures, and currently Emacs Lisp doesn't.)
> I never seen any interest to that.
Fair enough, but since you've never used closures and don't know what
they are, I doubt that your opinion should be a strong influence on
language design!
> And I never understoud why Tcl needs closures.
I don't know that it does, and I've even less idea of how closures
might be cleanly added to Tcl. It's clearly not necessary for a
dynamic (or whatever term you want to use to describe the category
that includes Tcl) language to have closures (emacs lisp doesn't, and
for a long time Perl didn't). On the other hand, most languages in
the same sort of category as Tcl now do have closures.
To be honest, I suspect it's simply too late for Tcl.
I think what you need is to know which things are code, and which
things aren't (so that you can find the variable and possibly other
references in those things which are code). But Tcl isn't like that,
and even fairly common things like callbacks in Tk seem to be
described in terms of string substitution and eval (in other languages
they'd be anonymous functions which would take the event or whatever
as an argument). (Other languages have eval, of course, but it plays
a much reduced role.)
My guess is that some kind of change like that might have happened if
the byte compiling work hadn't worked quite so darned well. But the
byte compiler *does* work well enough, and so there's no need to
consider the radical changes necessary to make compiling easier.
See my sketch for a closure in another subthread.
If these "externally declared" variables are indeed
meanto to be modifyable *and* persistent, replace the
generated "set" command (inside the foreach-loop) by
"static", which is implemented in the same posting.
[...]
> Miguel, after some sleep and pondering about every option, I think
> your design is the best we can get. With some minor change is roughly
> semantically powerful as lexical scoping, but trivial to understand
> and use:
>
> (define (foo)
> (let ((x 10))
> (lambda (y) (+ x y))))
>
> proc foo {} {
> set x 10
> lambda y {
> + $x $y
> } x
> }
>
> the 'x' in the lambda is a static var, the change I propose is that the
> intialization value for a static is mandatory, if you don't provide it,
> the value of the variable with the same name currently defined in the
> procedure creation context is used as intialization (anyway a static var
> without initialization hardly makes sense).
I don't understand what you mean. Do you mean that
proc foo {} {
lambda y {
+ $x $y
} x
}
would create a local x and give it the value of a variable x defined
when this "proc foo {} {" is found? That seems surprising. Why not
just do what Scheme and Lisp do, and have a default value, so (let ((x
1)) ...) defines a new binding with an initial value of 1, and (let
(x) ...) defines a new binding with an initial value of nil.
(Presumably for Tcl you'd use a zero length string as a default
value.)
> Another change could be to add a command that's able to modify from
> the "outside", the static vars of a procedure.
Probably that would make sense for Tcl. It's something that's not
done in Scheme as far as I know. Typically one defines several
functions with the lexical context, and those are the only functions
which can access the variables.
"Static vars" are only one kind of thing that closures might provide,
of course. (And most of those cases would normally be done with
namespaces, I suspect. Similarly objects make many uses of closures
uncompelling.)
[...]
[...]
> Let's look at some numbers. The following is from "top", having just
> started the programs and not done anything with them. Now, I'm using
> Tcl/Tk on a project, with some extensions we needed built into the
> interpreter. "dpwish" is the customized version used on the project.
>
> PID USERNAME THR PRI NICE SIZE RES STATE TIME CPU COMMAND
> 18870 dkick1 1 48 0 5440K 4120K sleep 0:00 0.00% lisp.run
> 18868 dkick1 5 58 0 3256K 2568K sleep 0:00 0.00% tclsh8.4
> 28393 dkick1 5 55 0 6200K 5144K sleep 0:00 0.24% wish8.4
> 14163 dkick1 1 54 0 4632K 3704K sleep 0:00 0.02% dpwish
Hmm. Here are some numbers from Debian unstable versions of things.
(I don't know which versions, but they're likely to be reasonably up
to date (Tcl is 8.4.9, for example), and reasonably similarly built.)
4608 3048 ion3
8640 3912 clisp
2280 996 lua
3048 1024 perl5
3004 1280 ruby
3212 1800 guile
10756 1324 tclsh
(ion3 is a window manager which links in with lua.)
So tclsh is reasonably small in resident size, but it doesn't look
especially impressive nowadays. I suspect competitors have caught up.
[...]
Sure they can - they just need to make the appropriate library
available. And guess what - they have to make other libraries available
for other things. Tk has to be there if they want GUIs (for the most
part), etc.
If the OO extensions were tough to find, that would be one thing.
But ActiveTcl has, as far as I know, _FOUR_ OO extensions in it - itcl,
XOTcl, and tcllib's stooop, and Snit.
If I am programming in C, I know that I have to download and install
various libraries if I want to perform various functions - X windows
libraries, Oracle libraries, etc.
If I am programming in C++, I know that I have to do the same.
If I am programming in Java, I have to locate and install the necessary
classes.
Certainly it would be great if at least one OO extension shipped with the
Tcl source. To date, the Tcl OO communities have not seen this as a high
enough priority to see through from the creation of a tip (http://tip.tcl.tk/)
to the incorporation into the source cvs.
--
<URL: http://wiki.tcl.tk/> MP3 ID tag repair < http://www.fixtunes.com/?C=17038 >
Even if explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.
<URL: mailto:lvi...@gmail.com > <URL: http://www.purl.org/NET/lvirden/ >
(ok, arrays should share their keys and should use Tcl_Obj* instead of
char* for them and other internal optimizations that are used in dicts,
but i do not really see a reason to eliminate arrays from the language.)
Dicts are cool, and first class, but why throw away a screwdriver
(arrays) just because we got a hammer (dicts) now? More tools in the box
are good, and four datatypes (array as variable collections, dict, list
and string as values) are not that many...
Michael
Isn't it clear to all that the problem I'm
pointing to is that Tcl arrays are not a proper data type? So, the
problem is not that "this special thing called Tcl arrays
are not first class", but that the Tcl fundamental language structure
supporting DICTIONARY operations are not a first class data type,
but instead a "collection of variables". Mind you, this thing
of "collection of variables" and not a type is a design limit/bug
of the intial versions of Tcl, because for the Tcl semantic the
simplest way to implement something like $a(foo) was this,
and evolved into a "feature" after things like traces were implemented.
So, call it in any way you like, but please try to at least agree about
what I propose should change:
Current tcl "array that are actually collection of variables", no longer
inside the core. Instead, $a(foo), and set a(foo) (note the subtle
difference, in theory the Tcl semantic only support substitution of
array elements, but the ability to change the value is a feature of
all the comands taking a variable name as argument because all this
commands are using the same C API, that have to *test* that the
variable name ends with ')' and contains a '(' before in the name).
But I was saying, that I propose that $a(foo) and set a(foo) to
be syntax sugar for [dict get] and [dict set] operations.
I hope that now at least what I (and many others) thing should be
the correct behaviour is clear.
>>Also, isn't it trivial with dicts
>>to create a couple of procedures that allows to
>>have traces on dict elements?
>
>
> Sure, you can set a write-trace on the variable holding
> the dict, and whenever a new value is written to the
> variable, you compare the new value with a backup of
> the old value, and fire registered callbacks for certain
> types of detected changes.
>
> there are some disadvantages to this approach:
> 1.) it is mindboggingly inefficient
> 2.) it needs double space (one extra copy to compare
> the dict value after changing)
> 3.) it may destroy objects stored in the dict, because
> for comparing untyped values, they need to be converted
> to string.
>
> All in all its surely no adequate substitute for arrays.
Because what you propose is a bad way to implement it.
Read later for one without this problems.
>>Just the exception (Tk programming with array traces) will
>>be where it's needed to call procedures, and all the rest
>>will be the case where you can use the syntax sugar $foo(bar).
>
>
> Yes adding the syntax sugar $dict(key) is a "good thing"(tm) :-)
> and should not have any problems with array & dicts coexisting,
>
> ...except that assigning to "d(key)" would not be allowed for
> a skalar variable d holding a dict.
To use the same syntax for two different things is a bad idea,
and of course like you say, there is no set d(key) operation
so it's totally inconsistent and possibly worse than today.
>>I mean you can define a procedure [myset dictVarName key value]
>>and [myget $dictValue key] that test for __read__trace__$key
>>and __write__trace__$key to check if there is an associated script
>>and then execute it.
>
>
> "set" is some levels too high to really intercept all
who wants to intercept set?
> changes to a variable. There are other commands that modify
> variables, such as lappend, append, incr, ... and thats why
> the hooks used by trace are founded a few levels deeper.
>
> I've got the impression you haven't fully understood traces
> and now try to reinvent them...
Maybe it's my problem and I don't understand Tcl very well.
But now I'll try to explain you what I meant with Tcl code
because we appear to be unable to talk in english:
--- start of script ---
proc mySet {dictVarName key val} {
upvar 1 $dictVarName dict
set ret [dict set dict $key $val]
catch {
if {[dict exists $dict __writetrace__ $key]} {
uplevel 1 [dict get $dict __writetrace__ $key]
}
}
return $ret
}
proc myGet {dictValue key} {
set ret [dict get $dictValue $key]
catch {
if {[dict exists $dictValue __readtrace__ $key]} {
uplevel 1 [dict get $dictValue __readtrace__ $key]
}
}
return $ret
}
proc mySetReadTrace {dictVarName key script} {
upvar 1 $dictVarName dict
dict set dict __readtrace__ $key $script
}
proc mySetWriteTrace {dictVarName key script} {
upvar 1 $dictVarName dict
dict set dict __writetrace__ $key $script
}
mySet a foo bar
mySet a name Salvatore
mySet a surname Sanfilippo
puts "Name: [myGet $a name]"
mySetReadTrace a name {puts Hello!}
mySetWriteTrace a surname {puts World}
puts "Name: [myGet $a name]"
mySet a surname antirez
---- end of script ----
Note: traces are not used at all.
The output of this script is:
Name: Salvatore
Hello!
Name: Salvatore
World
This is just some line of code, you can do much better
with this with some effort. So: can't just Tk users
(and we should all remember that's just a library... the
focus should be the language) use this kind of implementations?
There is just to write new code using an interface instead to
use syntax sugar, but being this a specific need it seems like
a minor effort.
Tk is just a library. Today is also not the best one at all, it's
unlike the first days of Tcl where this was an added value so big
to drive the language. Instead now it's likely to create more troubles,
many people think at Tcl not as a language, but with the mental
image of an old-looking GUI. If even important desing issues, like
to have a decent support for hashes, that every other language got
10 years ago, should be limited because of some *minor* problem with
Tk, we are lost.
May please some brillant guy that understand the language better
than me explain what's this big problem with Tk, and a number of
patterns that are possible with array traces and are not otherwise
possible? I bet other people will find a good solution for any
of this problems.
Maybe what you really need is instead an OOP environment so that you
don't have to fake design patterns using things like traces?
A lot of provocations, sorry, but I'm a bit tired to see this
language to get worse instead to get better because of non issues.
Regards,
Salvatore
> (ok, arrays should share their keys and should use Tcl_Obj* instead of
> char* for them and other internal optimizations that are used in dicts,
> but i do not really see a reason to eliminate arrays from the language.)
>
> Dicts are cool, and first class, but why throw away a screwdriver
> (arrays) just because we got a hammer (dicts) now? More tools in the box
> are good, and four datatypes (array as variable collections, dict, list
> and string as values) are not that many...
>
> Michael
Michael, one problem is that dict and arrays are a full duplication
modulo traces and upvar on single elements! so to take both may make
the language more complex than needed.
And there is a reason for the duplication, because array tried to be
since the first days something like dicts, but because of the bad design
they never reached that point. Then features like traces arrived into
the language making arrays something of special... bringing the current
situations we have :(
Ciao,
Salvatore
Exactly. While a procedure (anonymous) consists of {params body}, a
closure consists of {params body environment}. The environment is some
mapping from names to values. In Tcl, you might need two environments -
one for commands, one for variables, or a single environment might
maintain separate command and variable mappings. The questions are:
* Where to get the environment from,
* When to create the link (at runtime or definition time),
* What form the link should take (reference (mutable), or copy (value)).
There are various solutions to these questions which produce different
behaviours:
Lexical (static) Scoping:
- Get environment from lexically enclosing block (closure),
- Do this at definition time,
- Either reference (as in Scheme) or value (as in Haskell - ish).
Dynamic Scoping:
- Get environment from topmost stack frame at runtime.
Object Inheritance (static/class-based):
- Get environment from a designated super-class at definition time.
etc etc. There are lots of ways you can set this up.
Tcl already has a notion of a proc being associated with a namespace.
However, it's unfortunately just whatever namespace the proc happens to
be currently in, according to it's name, rather than being something
associated with the proc itself. You can see this behaviour if you
rename a proc across namespace boundaries -- it will probably stop working.
I have an exploration of some of the issues (there are many of them) at:
http://mod3.net/~nem/archives/2005/01/a_vision_for_tc.html
That essay contains some errors, and my current thinking has diverged
from there somewhat. But I think the basic ideas are still fairly sound.
Be warned though - some of what I suggest is fairly radical, and some of
it may turn out to be unworkable.
<snip>
>
>>And I never understoud why Tcl needs closures.
>
>
> I don't know that it does, and I've even less idea of how closures
> might be cleanly added to Tcl. It's clearly not necessary for a
> dynamic (or whatever term you want to use to describe the category
> that includes Tcl) language to have closures (emacs lisp doesn't, and
> for a long time Perl didn't). On the other hand, most languages in
> the same sort of category as Tcl now do have closures.
With namespaces, and with some of the more advanced object systems like
XOTcl, the need for closures is pretty small, if any. However, they are
a nice, conceptually small building block which you can make lots of
other cool stuff out of. To me, closures are worth investigating as a
possible mechanism for cleaning up and integrating how commands,
namespaces, objects, and ensembles are defined and interact. Once we
fully understand the issues we can decide what course to take.
>
> To be honest, I suspect it's simply too late for Tcl.
I hope not. It all comes down to how radical we want Tcl 9 to be. We
could break a lot of compatibility and end up with a really neat, clean,
solid language. But would it be a sufficient improvement to justify the
breakage? I think it probably would, but I'm prepared to accept I may be
in a minority.
>
> I think what you need is to know which things are code, and which
> things aren't (so that you can find the variable and possibly other
> references in those things which are code). But Tcl isn't like that,
> and even fairly common things like callbacks in Tk seem to be
> described in terms of string substitution and eval (in other languages
> they'd be anonymous functions which would take the event or whatever
> as an argument). (Other languages have eval, of course, but it plays
> a much reduced role.)
You *don't* have to be able to distinguish code and variables. Rather,
you do, but the question is when. If you can statically analyse code to
determine this, then you can resolve variable references at "compile"
time. But you don't need to, and you're right that it would never work
with Tcl. As I outlined above, you just need to keep a reference to the
enclosing environment, and then you can resolve names at runtime.
>
> My guess is that some kind of change like that might have happened if
> the byte compiling work hadn't worked quite so darned well. But the
> byte compiler *does* work well enough, and so there's no need to
> consider the radical changes necessary to make compiling easier.
Tcl code isn't friendly to static analysis. You'd probably have to give
up quite a lot to make it so.
-- Neil.
[...]
> If the OO extensions were tough to find, that would be one thing.
> But ActiveTcl has, as far as I know, _FOUR_ OO extensions in it -
> itcl, XOTcl, and tcllib's stooop, and Snit.
And presumably nowadays it's possible to load all of them into the
same tclsh. (It wasn't always so easy!)
> If I am programming in C, I know that I have to download and install
> various libraries if I want to perform various functions - X windows
> libraries, Oracle libraries, etc.
>
> If I am programming in C++, I know that I have to do the same.
>
> If I am programming in Java, I have to locate and install the necessary
> classes.
Sure, but once I've found an appropriata Java class, I can not just
use objects, I can subclass them, build my own classes providing those
interfaces, etc.
Similarly, if I get some XOTcl class I can easily use the objects, but
if I want to subclass them I need to do that using XOTcl operations
(presumably, anyway). And if I have an itcl class and I decide that
what I *really* want to do is use XOTcl style mixins to mess with it,
then presumably I'm out of luck.
Mostly things are OK, because I suspect that mostly we don't really
want object oriented things at all, we want what I'm sure was once
called "object based" things---i.e., the inheritance aspects of OO
aren't that important, much of the time.
[...]
> I don't understand what you mean. Do you mean that
>
> proc foo {} {
You probably forgot "set x 10" at this point.
> lambda y {
> + $x $y
> } x
> }
> would create a local x and give it the value of a variable x defined
> when this "proc foo {} {" is found? That seems surprising. Why not
Maybe it's unclear because I give an example with lambda without to
explain the normal behavior with a normal procedure:
basically:
proc foo {} {
incr x
puts $x
} {{x 10}}
will create a procedure 'foo' having 'x' as a "static" variable, i.e.
a variable that's persistent across calls to 'foo'. So:
puts "[foo] [foo] [foo]" will output:
11 12 13
Ok, now, because to declare a procedure with a static variable
without an intialization value does not make too much sense, we can
use this feature and make 'proc' to get the value of the variable
with the same name in the caller's context for every static varaible
where the default value is not specified.
so:
proc foo {} {
...
} {x y}
will raise an error if $x and $y variables are not defined at time of
proc creation. To specify default values is otherwise mandatory like in:
proc foo {} {
...
} {{x 0} {y 0}}
Because [lambda] is like proc, but anonymous it works the same way, so:
proc foo {x} {
lambda {y} {
+ $y $x
} x
}
if I call [foo 10], $x will value 10 when the [lambda] command is
called, so the 'static' variable 'x' will be initialized with this value.
This provides an approximation of syntactically scoped languages
for "free", i.e. without to make the behaviour of static vars complex.
> just do what Scheme and Lisp do, and have a default value, so (let ((x
> 1)) ...) defines a new binding with an initial value of 1, and (let
> (x) ...) defines a new binding with an initial value of nil.
> (Presumably for Tcl you'd use a zero length string as a default
> value.)
Because this way the code has to be written this way:
proc foo {x} {
lambda {y} {
+ $y $x
} [list x $x]
}
Not a big problem... mainly a matter of tastes.
>>Another change could be to add a command that's able to modify from
>>the "outside", the static vars of a procedure.
>
> Probably that would make sense for Tcl. It's something that's not
> done in Scheme as far as I know. Typically one defines several
> functions with the lexical context, and those are the only functions
> which can access the variables.
Yes because Scheme is *really* statically scoped. Because it's hard
enough to do it in Tcl, and it's a lot of efforts for something rarely
used even in scheme, I think that an acceptable way to share a context
is that some function knows the name of the other.
Returning to the previous example, but this time returned *two*
functions, one that does the increment and one that changes the
increment performed by the first function:
proc foo {x} {
set f [lambda {y} {+ $y $x} x]
set g [lambda {incr} {closure set $f x $incr} f]
list $f $g
}
so 'g' is able to change the closure of 'f', [foo] will
return this couple of functions, one to do the work, and the
other one to tune the work of the first.
I think that given the fact that with this design things are
so simple, and still it's possible even to approximate lexical
scoping in non trivial cases, it's pretty acceptable.
Most of the times Tcl users will use it in a different way
I bet, i.e. to take state for command based interfaces, to avoid
globals when the state is only about a single procedure,
or to return specialized lambdas when lambda will be available.
> "Static vars" are only one kind of thing that closures might provide,
> of course. (And most of those cases would normally be done with
> namespaces, I suspect. Similarly objects make many uses of closures
> uncompelling.)
Yes it is surely true that a good object system makes most uses
of closures a non issue.
Similarly the "static vars" can be used to implement yet another OOP
system :)
Ciao,
Salvatore
> And there is a reason for the duplication, because array tried to be
> since the first days something like dicts, but because of the bad design
> they never reached that point.
I kind of disagree. Arrays as collections of variables are more like a
very poor man's object without explicit methods. So yes, they are bad
design, but i do not think they should be replaced directly by dicts,
but by a well thought out object system, with could provide features
like traces on member variables in a clean way without the extra (and
strange) complexity of traces.
Michael
The values of the static/local values should be stored somewhere that
isn't lost on shimmering.
Storing these values in a namespace feels wrong to me. It should be
stored such that it's part of the command data.
When running the command with a local variable (which changes the value
of that variable) you are, in effect, changing the command. Each time
the command is run, it does something different. In effect, the command
IS a different value each time you run it. As such, having it change
it's string rep/value makes sense to me. I understand it may feel a bit
"un-Tclish", but no more so that objects that change their own state.
As for the part about it not being "how the dual rep works", can you
explain further? To me, it seems exactly like:
set x 1 ;# here we have the string rep
incr x ;# the string rep is no longer valid now
puts "X: $x" ;# here the string rep is recreated for hte new value
Rob Seeger
Some comments below
antirez wrote:
<snip>
> mySet a foo bar
> mySet a name Salvatore
> mySet a surname Sanfilippo
>
> puts "Name: [myGet $a name]"
>
> mySetReadTrace a name {puts Hello!}
> mySetWriteTrace a surname {puts World}
>
> puts "Name: [myGet $a name]"
> mySet a surname antirez
>
> ---- end of script ----
>
> Note: traces are not used at all.
>
> The output of this script is:
> Name: Salvatore
> Hello!
> Name: Salvatore
> World
>
> This is just some line of code, you can do much better
> with this with some effort. So: can't just Tk users
> (and we should all remember that's just a library... the
> focus should be the language) use this kind of implementations?
> There is just to write new code using an interface instead to
> use syntax sugar, but being this a specific need it seems like
> a minor effort.
The problems as I see them are:
1. You have to write mySet/myGet methods which understand traces, and
trace setters *for each new datatype*.
2. It all falls apart when someone manipulates your dictionary (or
whatever) using list commands (or string commands, etc).
Variable traces are a single mechanism which handles this -- and handles
it at the right level: the variable. It doesn't make sense to set traces
on dictionary elements, because they never change. The alternative
you've proposed is to set traces on particular operations. It's not the
same thing though.
Dictionaries and arrays are different things. You can't chuck out arrays
for dictionaries. You *could* (and I've argued before that we should)
chuck out arrays in favour of namespaces, but Tcl's namespaces are a bit
of a mess, and quite a lot more heavyweight than arrays.
You could add new syntax for convenient dict indexing, e.g.:
set d [dict create {name Neil age 24}]
puts $d.name
puts $d.age
But I would not try and reuse $() notation - it's just confusing.
I'd like to point once again to TOOT (http://wiki.tcl.tk/TOOT). If we
had auto-{expand} of leading word then you could do:
proc dict: {dict key} {
dict get $dict $key
}
proc make-dict {args} {
return [list dict: [dict create {expand}$args]]
}
set d [make-dict name Neil age 24]
$d name ;# Neil
$d age
Which actually saves you one character over $() syntax.
<snip>
> Maybe what you really need is instead an OOP environment so that you
> don't have to fake design patterns using things like traces?
I think you have severely misunderstood the capabilities of traces. They
represent a nice separation of concerns -- you register interest in
changes with things that can actually change (variables) rather than
having to code notifiers into every data type API. Compare for instance
to explosion of *Listener classes in most Java APIs.
-- Neil.
I'm leaving out the rest of Damien's post on the grounds that its either
a retread of what he said here, discussion of the size of things (which
is hard to compare) or things that feel like a set of cheap jibes backed
up by a blizzard of links. :^) This leaves an interesting set of
functionality that is not there right now:
* Big-ints will be in 8.5 as we've found a library that does what we
want with a suitable license. I can't see the TCT voting against
this, and Kevin Kenny's in charge.
* Fractions (you mean rationals?) are fairly special-use IME, but YMMV.
* Lexical closures are interesting, but I'll let the other sub-thread
discuss them. :^)
* An object system. Yeah, we know. But why this insistance on "multiple
dispatch with a metaobject protocol"? Multiple dispatch tends not to
be done in the "OO in Tcl" community, since MD is a strongly-typed
approach, and reading about MOPs in general at:
http://www2.parc.com/csl/groups/sda/projects/mops/default.html
I'd say that such methods of operating are quite reasonable in Tcl
(yes, we like introspection!) They're just not given such a
high-falutin' name like MetaObject Protocol. :^)
So, that's an "On it already", a "don't care", a "hot topic", and a
"we've actually done that ages ago with extensions". :^D Indeed, it's
quite possible to do acceptable OO in pure Tcl scripts.
Donal.
antirez wrote:
> Returning to the previous example, but this time returned *two*
> functions, one that does the increment and one that changes the
> increment performed by the first function:
>
> proc foo {x} {
> set f [lambda {y} {+ $y $x} x]
> set g [lambda {incr} {closure set $f x $incr} f]
> list $f $g
> }
>
> so 'g' is able to change the closure of 'f', [foo] will
> return this couple of functions, one to do the work, and the
> other one to tune the work of the first.
This, I think, is where there might be a problem. At this point, the
static variables involved need to be stored somewhere outside the value
of the lambdas. I question where it would be stored, how garbage
collection would be handled, and what happens with shimmering.
> I think that given the fact that with this design things are
> so simple, and still it's possible even to approximate lexical
> scoping in non trivial cases, it's pretty acceptable.
>
> Most of the times Tcl users will use it in a different way
> I bet, i.e. to take state for command based interfaces, to avoid
> globals when the state is only about a single procedure,
> or to return specialized lambdas when lambda will be available.
This is exactly my thought... We don't need the full power of closures,
but just that of proc-local/static variables + lambda. Any use case I've
seen can be solved by those capabilities. Past that, and we're adding
solutions looking for a problem. I don't think its worth the effort.
Rob Seeger
Hello Neil!
> The problems as I see them are:
>
> 1. You have to write mySet/myGet methods which understand traces, and
> trace setters *for each new datatype*.
But here the concern is only about traces on array elements.
That's the only traces that are no longer possible if arrays go
away and dicts are used as replacement.
I expect to see dict-like access patterns in code using traces
on array elements, so you just need this mySet/myGet implementation.
For example... you may have a widget that shows a matrix of values,
and you take the matrix elements inside an array, with traces
to update the widget every time something try to "set matrix(1,2) 5".
with myGet/mySet you can do the same of course.
> 2. It all falls apart when someone manipulates your dictionary (or
> whatever) using list commands (or string commands, etc).
Well... traces falls apart if an external user delete the trace :)
What I mean is that if a program is written to use an interface,
it should be used.
> Variable traces are a single mechanism which handles this -- and handles
> it at the right level: the variable. It doesn't make sense to set traces
> on dictionary elements, because they never change. The alternative
> you've proposed is to set traces on particular operations. It's not the
> same thing though.
But often, in the case of traces on array elements, there is this kind
of operation-based behaviour. The idea is that when you set/get
a key (yes, it's a var... but I mean the programmer often mean to
get/set a key instead), an associated operation is performed.
So you can model the same design using procedures to create another
layer to variables access.
> But I would not try and reuse $() notation - it's just confusing.
Modulo 'operations on variables', dictionaries are fully backward
compatible with arrays if the $() notation is used.
Just they have more useful features (first class, nesting).
So what's confusing? People that are advanced programmers enough
to use traces will not have troubles. All the rest will not see
the difference at all in old code.
> I'd like to point once again to TOOT (http://wiki.tcl.tk/TOOT). If we
> had auto-{expand} of leading word then you could do:
>
> proc dict: {dict key} {
> dict get $dict $key
> }
> proc make-dict {args} {
> return [list dict: [dict create {expand}$args]]
> }
> set d [make-dict name Neil age 24]
> $d name ;# Neil
> $d age
Sure tehre are plenty of alternatives. The closures topic itself
is perfect to mimic this.
set foo [get-dict-acess-proc $dictValue]
get: [$foo age], set: [$foo age 25], and so on
but why? my view is, we have a very poor feature
in the language (arrays), we need to replace they
with the good thing.
>> Maybe what you really need is instead an OOP environment so that you
>> don't have to fake design patterns using things like traces?
>
> I think you have severely misunderstood the capabilities of traces. They
> represent a nice separation of concerns -- you register interest in
> changes with things that can actually change (variables) rather than
> having to code notifiers into every data type API. Compare for instance
> to explosion of *Listener classes in most Java APIs.
Neil what you will no longer have is *only* the ability to set traces
on array elements, all the rest will stay.
My comment on OOP is not that OOP is a replacement for traces, what I
suspect is that instead traces are used in a context where instead
widgets and other state is better encapsulated inside an object that
also mediates for GUI updates and so on.
But because we miss OOP people will create new patterns using what they
have, i.e. traces on array elements to fake objects.
In the end, what arrays have to offer more than dicts?
traces on elements.
what dicts have to offer more than arrays?
nesting
passed as argument, returned by procedures (i.e. first class)
serialization for free
more speed
to have both is a good idea?
not at all, in my opionion. They are a duplication, but arrays
sucks compared to dicts. Compatibility concerns? there must be
a version of Tcl where one can break compatibility.
Of course the real reason can just be that I'm the only not
happy with the current behaviour :) and this is indeed the
only strong reason to take the current way ;)
Ciao,
Salvatore
--
Salvatore Sanfilippo <antirez at invece dot org>
We're programmers. Programmers are, in their hearts, architects -- Joel
Spolsky
http://www.invece.org
I was talking about namespace as in a mapping from names to values
(well, plus other stuff needed for traces, links etc), rather than the
hierarchical system exposed at the script level. So, you could have the
namespace attached to the command, rather than the name in the hierarchy
-- this is in fact, almost certainly the correct thing to do.
>
> When running the command with a local variable (which changes the value
> of that variable) you are, in effect, changing the command. Each time
> the command is run, it does something different. In effect, the command
> IS a different value each time you run it. As such, having it change
> it's string rep/value makes sense to me. I understand it may feel a bit
> "un-Tclish", but no more so that objects that change their own state.
Values are not objects. Don't be fooled by the name Tcl_Obj -- it's only
called that because Tcl_Value was already taken.
>
> As for the part about it not being "how the dual rep works", can you
> explain further? To me, it seems exactly like:
> set x 1 ;# here we have the string rep
> incr x ;# the string rep is no longer valid now
> puts "X: $x" ;# here the string rep is recreated for hte new value
You are confusing values and variables, and also implementation details
with the script-level semantics.
1. set x 1
This creates a variable, with the name "x", and stores a value with the
string rep "1" in it.
2. incr x
This takes the value that is in the variable "x", adds 1 to it, and
stores the *new value* back into the variable. As an *implementation
optimisation* Tcl may alter the Tcl_Obj in place (which will invalidate
the string rep), but only if the value is not shared. The Tcl programmer
should not have to be concerned with these details, and they shouldn't
be in any way visible at the script level.
3. puts "X: $x"
The string rep of the value that is now in the variable "x" is
displayed. It may be generated if it didn't already have one.
As an example of why what you are proposing is not a good idea, consider
the following:
set scope {a 1 b 2}
set cmd1 [lambda {x} { set a $x } $scope]
$cmd1 12
puts "scope = $scope"
The value stored in the scope variable is also used as the scope for the
lambda (the static vars). With your modify-in-place semantics, this code
would produce the result:
scope = a 12 b 2
or similar. I don't think that is a good idea. It is certainly very
different to what Tcl currently does.
-- Neil.
Proper shimmering never loses a value. At most it might lose
a particularly efficient representation of the value.
If a value is getting lost, either refcounting or a Tcl_ObjType
implementaion contains errors.
--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|
Well I don't know in Tcl, I can say how I handle it in Jim.
in Jim I've a reference system similar to 'TclRef' I wrote for Tcl
(check the wiki for it), but that does not have the 'fragile references'
problem because every live Tcl_Obj is in a linked list with head
in the interp structure. So Jim's lambda is implemented in Tcl itself:
# Lambda with GC
proc lambda {arglist body} {
set name [ref {} lambdaFinalizer]
proc $name $arglist $body
return $name
}
proc lambdaFinalizer {name val} {
rename $name {}
}
it's just a proc that has as name the reference created.
the [ref] command creates a reference with the first
argument as value of the reference (here it is not used
so it's an empty string), and the finalizer (a procedure
that will be called with the reference is going to
be collected), as second argument.
So when this reference is no longer found in the whole
Tcl_Objects space of the interpreter, rename $name {} will
destroy the procedure.
Because a procedure can't shimmer, there is no shimmering
problem.
>> Most of the times Tcl users will use it in a different way
>> I bet, i.e. to take state for command based interfaces, to avoid
>> globals when the state is only about a single procedure,
>> or to return specialized lambdas when lambda will be available.
>
>
> This is exactly my thought... We don't need the full power of closures,
> but just that of proc-local/static variables + lambda. Any use case I've
> seen can be solved by those capabilities. Past that, and we're adding
> solutions looking for a problem. I don't think its worth the effort.
agreed.
[...]
> You *don't* have to be able to distinguish code and
> variables. Rather, you do, but the question is when. If you can
> statically analyse code to determine this, then you can resolve
> variable references at "compile" time. But you don't need to, and
> you're right that it would never work with Tcl. As I outlined above,
> you just need to keep a reference to the enclosing environment, and
> then you can resolve names at runtime.
Yes, I think you're right. I hadn't thought of that kind of solution
before, but I think I see how you could make it work.
So (presuming that lambda is the important thing) you need some place
to keep an environment, and that place needs (potentially) to be
shareable. For example, if you have:
proc foo {} {
set one [lambda {...} {...} {x 0}]
set two [lambda {...} {...} {x 0}]
}
then I think you want both lambdas to share the same x. Hmm, maybe I
have the wrong syntax, or maybe it's intended that this kind of syntax
is just to create static variables for functions?
If so, I can see the value in that kind of thing, but it's not really
what I think of when "closure" is mentioned.
[...]
I would go farther. I don't know that it's really that useful to have
that kind of closure. The closures I've used access primarily the local
variables of the enclosing procedure.
proc iterate {lambda list} {
foreach i $list {
$lambda $i
}
}
proc addup {list} {
set result 0
iterate [lambda {value} {incr result $value}] $list
return $result
}
Without the lambda's ability to get to addup's "result" variable,
whether you carry around an environment is far less useful. Indeed, I
suspect without thinking too much about it that this is the primary
difference between "objects" and "closures".
--
Darren New / San Diego, CA, USA (PST)
He had a name like someone sneezing with
a mouth full of alphabet soup...
> I would go farther. I don't know that it's really that useful to have
> that kind of closure. The closures I've used access primarily the
> local variables of the enclosing procedure.
>
> proc iterate {lambda list} {
> foreach i $list {
> $lambda $i
> }
> }
>
> proc addup {list} {
> set result 0
> iterate [lambda {value} {incr result $value}] $list
> return $result
> }
>
> Without the lambda's ability to get to addup's "result" variable,
> whether you carry around an environment is far less useful. Indeed, I
> suspect without thinking too much about it that this is the primary
> difference between "objects" and "closures".
Yes, I agree. In fact my first draft had this:
proc foo {} {
set x 2
set one [lambda {...} {...} {x 0}]
set two [lambda {...} {...} {x 0}]
}
but then it's not at all clear why I'm required to initialise x as
part of the lambda syntax, since if x already exists in the proc, then
I can just initialise it as I would any other variable. (I now notice
I've possibly left out some braces, but let's ignore that.) So
possibly it should be:
proc foo {} {
set x 2
set one [lambda {...} {...} x]
set two [lambda {...} {...} x]
}
and probably I'd want to do things like:
set c 0
proc count {} {
incr c
} c
with, I imagine, some way to restrict c to a scope of some kind?
Maybe that's not essential. That all seems feasible. Of course, once
I had that, I'd want everything that currently uses a script to
change, so I could use closures:
set c 0
button .c -command [lambda {event} {incr c}]
label .m -textvariable c
Oh, and I want some way to say that the c there is really the closure
c, not some random global variable called c. So I guess a bit more
syntax would be needed. Hmm, "label .m -textvariable :c"?
Tcl is superb in my experience. What would make my
personal coding more pleasant would be some
"syntactic sugar"
1. Sreamlined way to invoke expr
2. Sreamlined way to do a few list & string ops, primarily lindex, string
range, etc.
3. Smooth a few inconveniences:
- incr a non-exising var to create it
- Switch with "fall thru" option would be nice
These are old war horses that can be reviewed on the wiki or
newsgroups, and TIP list...
I've made peace with all this by using add-on macro processing.
>
> So, if you answer this, please let me know if 8.5 or the 9 wishlist
> would fix what you currently consider "wrong" with Tcl as that would be
> good to know for myself.
>
> I am just trying to get a feel for what you guys the Tcl community
> think about the language as a whole.
>
> Thanks,
>
> Robert
>
The problem is there, even if you limit to dict (as they can
in your opinion be made a full substitute for arrays).
There are a couple of ways to get a different dict from a
given dict. Even [dict set ...] does not really modify
a dictionary (well, under some circumstances it is possible
that it actually happens that way, but definitely not always)
but instead it is a convenience-function that writes back
the derived dict to the variable previously holding the
original.
set d [dict create 0 a 1 b]
dict set d 1 c
does with dicts the same thing, that
set l [list a b]
set l [lreplace $l 1 c]
does with lists (which just like dicts are also
immutable objects)
Now there are 2 issues:
1.) dict set is not the only one of these convenience-operations,
but there are also: append,incr,lappend,unset,update,with
each subcommands of dict. But ok, one could surely rewrite
each of them similarly to your "rewrite" of [dict set ...]
2.) there are a couple of directly value based dict-operations,
(not mentioning the readonly-ops): filter,merge,remove,replace
these do not write back the resulting dict to the original
variable, but only return it, leaving it to the user to
whatever he likes with it (perhaps even ignore it)
now, assume I've got a variable containing a dict, and using your
value-change-callbacks, I "install" a write-trace on key 1:
% set d [dict create 0 a 1 b]
% # modulo typos and perhaps forgotten extra parameters:
% dict set d __write_callback__ 1 {puts "element 1 changed"}
Now, dict set d 1 c (if it were changed to behave like your proposal)
would trigger the callback, fine.
But, now, I need to [dict replace ...] a few values, passing the
resulting dict to a procedure:
% mycommand foo [dict replace $d 1 c 3 x 4 y]
This would (from dict replace's view) not modify "d".
So do we still want [dict replace ] to fire a callback, or not?
[dict replace] does not know, if "mycommand foo" might actually
be "set d" instead, but then we *would* indeed "modify" the original
dict-variable d.
But then again, there may be other stuff happening before we
write back the dictionary:
% set d [ ... a dict with {0 a 1 b} and a write-callback for "1" ...]
% set x [dict replace $d 1 c 3 x 4 y]
% ...
% # set d "something else"
% ...
% set d $x ;# now, d is like it had its value for "1" changed.
Where exactly during this process would you propose to make
changes to trigger the callback ?
Change "set" to detect that the var newly receives a dict-value
with a write-callback-item in it?
Then, how would you prevent to fire the callback during
set'ing "x" in second line?
So, you'd check if the newly assigned dict is in some way
remotely similar to the one previously stored? (like I
mentioned with variable-trace on "d", but without trace, but
perhaps a rewrite/variant of "set", which would save us from
the extra problem of maintaining the backup to compare with)
> In the end, what arrays have to offer more than dicts? traces
> what dicts have to offer more than arrays? lotsa stuff
> to have both is a good idea?
Sorry, *yes*.
At least until some brilliant person comes up with a working
trick to unify them. I wouldn't call that impossible, but it
would require some extra bit, that's yet missing.
Perhaps mutable (and thus traceable) but anonymous objects
as in ocaml, but I wouldn't bet on that this can be done
*sanely* in Tcl.
> Of course the real reason can just be that I'm the only not
> happy with the current behaviour :) and this is indeed the
> only strong reason to take the current way ;)
Traces do have some woes, but you haven't even addressed those :-)
- a better way to pass along meta-information.
rather than always append two or three parameters use
bind-style "%"+char at any place inside the callback
script that would be replaced by the appropriate infos.
- be able to (explicitly) request a "backup-copy" before a
variable is overwritten. (that's more about elegance
than about making thing possible that weren't yet.)
- cooperation with GUI: traces fire immediately, but
GUI has to wait for event-loop. Doing "update idletasks"
in trace-handler is a *BAD THING*.
- doing multiple (independent) traces on one variable sucks.
There exist helpers in that direction (I think "wcb"
by Csaba Nemethi was something similar), but they
aren't "standard". "after" behaves much better in
this way.
None of these issues is likely to be solved any near time.
OK.
> I expect to see dict-like access patterns in code using traces
> on array elements, so you just need this mySet/myGet implementation.
>
> For example... you may have a widget that shows a matrix of values,
> and you take the matrix elements inside an array, with traces
> to update the widget every time something try to "set matrix(1,2) 5".
> with myGet/mySet you can do the same of course.
Sure, you can.
>
>> 2. It all falls apart when someone manipulates your dictionary (or
>> whatever) using list commands (or string commands, etc).
>
>
> Well... traces falls apart if an external user delete the trace :)
> What I mean is that if a program is written to use an interface,
> it should be used.
That's not the same thing at all. The point I was trying to make before
is that all these APIs are about the same thing -- determining when
something changes. What I am arguing for is that we reduce the "things
that can change" to just one thing: variables (maybe commands too, if we
want to keep the naming distinction). That way you have a single
interface for listening for changes.
The direction you are going in results in a proliferation of interfaces
to detect changes to different types of data structure. I think the
better solution to this problem is to find a way to put variables into
data structures, e.g. to put a variable into a dict. That way you can
register traces on the variable, while getting the benefits of a
dictionary. But here we have the main problem: variables are not first
class values (for obvious reasons), and don't have a natural string rep
which could be used to reconstruct them.
So, what are the solutions?
1. Have separate data structures which contain variables rather than
values, and know how to manipulate them. This is the situation we have
now with arrays and namespaces.
2. Let each data structure provide variable-like functionality, e.g.
trace callbacks. This is what you have proposed above (only for dicts,
but still; I think it is not a good general solution).
3. Provide named references which can be passed around and stored in
data structures.
My feeling is that Option 2 is sub-optimal for the reasons already
given, and that Option 3 implies a variation of Option 1 (as I will
explain below), but that we can drop arrays (but not in favour of dicts).
The only way to achieve (3) is to provide some string rep name which can
be used to lookup the current value (this indirection is absolutely
necessary). *We already have this in Tcl today* -- just use the
(fully-qualified) name of a variable:
set ::myvar 24
set d [dict create a 13 b ::myvar]
set [dict get $d b] 24
This works fine. In order to do this though, you need to be able to look
up the current value of ::myvar somewhere -- in this case, in a
namespace. The *mutable* container that is used to look up the var
cannot be a dict, because it needs to be mutable in-place. So, we still
need some mutable hash-table type which isn't first-class. My preference
is that a cut-down, cleaned up namespace is better for this than an
array, and that we can chuck arrays out.
Once (if) you've thrown arrays out you could start thinking about a
reuse of the () syntax, but I'd prefer just to drop it completely.
>
>> Variable traces are a single mechanism which handles this -- and
>> handles it at the right level: the variable. It doesn't make sense to
>> set traces on dictionary elements, because they never change. The
>> alternative you've proposed is to set traces on particular operations.
>> It's not the same thing though.
>
>
> But often, in the case of traces on array elements, there is this kind
> of operation-based behaviour. The idea is that when you set/get
> a key (yes, it's a var... but I mean the programmer often mean to
> get/set a key instead), an associated operation is performed.
They meant to get/set a *variable* under some key -- we need a clean
separation of naming (what dicts do) from mutable state (what variables
do). Variables just happen to need to be named in order to preserve
everything-is-a-string.
>> I'd like to point once again to TOOT (http://wiki.tcl.tk/TOOT). If we
>> had auto-{expand} of leading word then you could do:
>>
>> proc dict: {dict key} {
>> dict get $dict $key
>> }
>> proc make-dict {args} {
>> return [list dict: [dict create {expand}$args]]
>> }
>> set d [make-dict name Neil age 24]
>> $d name ;# Neil
>> $d age
>
>
> Sure tehre are plenty of alternatives. The closures topic itself
> is perfect to mimic this.
>
> set foo [get-dict-acess-proc $dictValue]
>
> get: [$foo age], set: [$foo age 25], and so on
> but why? my view is, we have a very poor feature
> in the language (arrays), we need to replace they
> with the good thing.
The difference between the closures and TOOT is that the TOOT version is
a real first-class value (which is why I only provided a get op). The
closure (if it is mutable) *must* be named. The point of the example was
that you don't need to overload/override the $() syntax to get
convenient access to dictionary elements.
>
>>> Maybe what you really need is instead an OOP environment so that you
>>> don't have to fake design patterns using things like traces?
>>
>>
>> I think you have severely misunderstood the capabilities of traces.
>> They represent a nice separation of concerns -- you register interest
>> in changes with things that can actually change (variables) rather
>> than having to code notifiers into every data type API. Compare for
>> instance to explosion of *Listener classes in most Java APIs.
>
>
> Neil what you will no longer have is *only* the ability to set traces
> on array elements, all the rest will stay.
That's fine.
> In the end, what arrays have to offer more than dicts?
>
> traces on elements.
mutable state.
>
> what dicts have to offer more than arrays?
>
> nesting
> passed as argument, returned by procedures (i.e. first class)
> serialization for free
> more speed
>
> to have both is a good idea?
My argument is not that arrays shouldn't be removed (I think they
probably should), but that the proper replacement for them is
namespaces, not dictionaries. Trying to reuse () notation for
dictionaries just confuses the issue.
Cheers,
Neil.
My point was that the local variables and values for the proc have to be
stored in such a way that they (or the way to get to them) are part of
the data, rather than part of some metadata stored with the Tcl_Obj.
Rob Seeger
Neil Madden wrote:
> Robert Seeger wrote:
>
>> My thoughts on this consist of the following:
>>
>> The values of the static/local values should be stored somewhere that
>> isn't lost on shimmering.
>>
>> Storing these values in a namespace feels wrong to me. It should be
>> stored such that it's part of the command data.
>
>
> I was talking about namespace as in a mapping from names to values
> (well, plus other stuff needed for traces, links etc), rather than the
> hierarchical system exposed at the script level. So, you could have the
> namespace attached to the command, rather than the name in the hierarchy
> -- this is in fact, almost certainly the correct thing to do.
Fair enough. Along those lines, how is the "location" of the metadata
determined, and how would it be garbage collected when the lambda/proc
goes away?
>
>>
>> When running the command with a local variable (which changes the
>> value of that variable) you are, in effect, changing the command. Each
>> time the command is run, it does something different. In effect, the
>> command IS a different value each time you run it. As such, having it
>> change it's string rep/value makes sense to me. I understand it may
>> feel a bit "un-Tclish", but no more so that objects that change their
>> own state.
>
>
> Values are not objects. Don't be fooled by the name Tcl_Obj -- it's only
> called that because Tcl_Value was already taken.
I wasn't implying they were. I was just drawing an analogy to the use of
an object system in Tcl. When you call a setter for an object, you are
changing the "value" of the object you are dealing with
$myObject setX 5
>> As for the part about it not being "how the dual rep works", can you
>> explain further? To me, it seems exactly like:
>> set x 1 ;# here we have the string rep
>> incr x ;# the string rep is no longer valid now
>> puts "X: $x" ;# here the string rep is recreated for hte new value
>
>
> You are confusing values and variables, and also implementation details
> with the script-level semantics.
I'm not confusing it, I'm just choosing to ignore it for this. Along the
same lines, I can see an object system where the value of the variable
the user has for the object IS the object.
set myObject [createAnObject]
puts $myObject
... the output is everything about the object ...
$myObject setX 5
puts $myObject
... this output is different than above ...
> As an example of why what you are proposing is not a good idea, consider
> the following:
>
> set scope {a 1 b 2}
> set cmd1 [lambda {x} { set a $x } $scope]
> $cmd1 12
> puts "scope = $scope"
>
> The value stored in the scope variable is also used as the scope for the
> lambda (the static vars). With your modify-in-place semantics, this code
> would produce the result:
>
> scope = a 12 b 2
I disagree. The output would be "a 1 b 2" as one would expect. Modifying
the lambda would change the lambda, not the values fed to it in its
creation. When the lambda is first created, the refcount on $scope would
be incremented by one. When the lambda changes, a copy of the scope
would need to be made, the same as if you did:
set scope {a 1 b 2}
set list1 [list {x} { set a $x } $scope]
lset list1 2 {a 12}
Robert Seeger
Thanks to Bruce Stephens & Neil Madden for these explanations.
I've two feelings about that:
- environment is a complex idea and beginners take some time to
understand that the execution environment can be different of the
definition environment (what means [namespace::proc $arg] is not an
evidence)
- mastering the environment in Tcl is matter of using namespaces (as
substitutes of objects).
ulis
I'm not entirely happy with that. I don't like listing the variables at
the end of the declaration like that; that's definitely going to cause
problems ("Where did I declare this? Oh, at the _end_...") and is in
any case utterly different to the way that Tcl works (strict in-order
processing) even if people would (rightly) point out that that doesn't
technically apply in this case. It certainly feels like it *ought* to
apply though. :^)
I'd much rather introduce scoped variables explicitly and have them
then picked up by [proc] (and [lambda], a separate issue)
automatically. This might be done a bit like this (in that second
example):
scopeVars c {
set c 0
proc count {} {
incr c
}
}
OK, how might this be implemented? (Without at least a plan for
implementation, any idea is moonshine). Well, my idea is that each
stack frame will have a list of these extra scopes, and that these
scopes are reference counted and sharable. The [scopeVars] command
creates a new scope, marks that scope as having some variables, pushes
that scope onto the stack frame's scope list (it's a scope list so that
[scopeVars] is sensibly nestable), executes the body argument, and then
pops that scope off the list again. The [proc] command retains a
reference to all scopes in the current stack frame's scope list in the
commands it creates, which pre-push those scopes onto the stack frames
created when executing. When resolving a variable name, the current
list of scopes is checked first. Both [upvar] and [uplevel] will work
as expected, as will [trace] (probably; I'm not quite sure about delete
traces as they might trigger when the variable doesn't have a name that
is currently locally visible at all. Potentially at least.)
Apart from the syntactic advantages outlined earlier, there are
semantic bonuses too. For example, we only share the variables that we
say we're sharing. This minimizes the breakage for existing code, keeps
the amount of variable name clashing down (a topic I can write about
more some other time) and makes it easier to control memory usage as
well (not so many leaking bits of random state.)
We probably need other bits too. I'm not claiming that this is a
complete plan, or even that I've got the best name for everything. :^)
> Maybe that's not essential. That all seems feasible. Of course,
once
> I had that, I'd want everything that currently uses a script to
> change, so I could use closures:
> set c 0
> button .c -command [lambda {event} {incr c}]
> label .m -textvariable c
> Oh, and I want some way to say that the c there is really the closure
> c, not some random global variable called c. So I guess a bit more
> syntax would be needed. Hmm, "label .m -textvariable :c"?
Hmm, to me that says that we'd want to resolve variables against
scopes-or-namespaces, and there are some complexities relating to how
do you set a variable that doesn't currently have a locally-visible
name. But that's the toughest issue. (As I said earlier, traces
themselves should just work.) Clearly this would require new API, and
it encourages a significant enough change in the way of working with
scripts that I'd suggest that this ought to be a major-version-only
change. (e.g. for Tcl 9.0 or Tcl 10.0).
Donal.
> I've two feelings about that:
> - environment is a complex idea and beginners take some time to
> understand that the execution environment can be different of the
> definition environment (what means [namespace::proc $arg] is not an
> evidence)
> - mastering the environment in Tcl is matter of using namespaces (as
> substitutes of objects).
I think you're oversimplifying what happens in Tcl.
Tcl has (optionally) a kind of dynamic scope, in the sense that a
procedure can get hold of variables from the stack of procedures that
called it.
That tends to be easy to understand, because it's almost always used
to access an array or variable in the procedure that called you, so
you do things like:
proc foo {arrVar} {
upvar 1 $arrVar array
set array(something) something
}
In theory one can do "upvar 17 $a b" or even "upvar $n $a $b" and
stuff, but as far as I know that kind of thing is only ever going to
be useful for debugging, where you *know* you're doing unusual things.
I think users need to learn the current environment, the global
environment, and namespaces. You can't do without understanding the
global environment because so many things happen in it.
Arguably it's possible to avoid some of this in a language with proper
lexical scoping. In those, a name which doesn't have a more local
binding is a global variable, so you can do:
proc foo {} {
set x 1
label .c -textvariable x
}
and that kind of thing, and the two x's are the same variable since no
other binding for x has been created. Of course in Tcl you really
have to do:
proc foo {} {
set ::x 1
label .c -textvariable x
}
(It would also be legal to use ::x in both places, of course.) (You
can also use "global x" to indicate that the x is in the global scope,
but that seems less readable to me than marking each time using "::".)
Anyway, I'm not at all convinced that Tcl's semantics are particularly
simple in this respect. I'm not sure that any other way of doing
things is particularly simpler; lexically scoped languages tend to
want to have *some* dynamically scoped names, too, and then you tend
to have other scopes like packages or namespaces or objects.
> I'm leaving out the rest of Damien's post on the grounds that its
> either a retread of what he said here, discussion of the size of
> things (which is hard to compare) or things that feel like a set of
> cheap jibes backed up by a blizzard of links. :^)
Good, I can feel your anger... Use it. Pick up your lightsaber...
> This leaves an interesting set of functionality that is not there
> right now:
>
> * Big-ints will be in 8.5 as we've found a library that does what we
> want with a suitable license. I can't see the TCT voting against
> this, and Kevin Kenny's in charge.
> * Fractions (you mean rationals?) are fairly special-use IME, but
> YMMV.
Yes, rational; but rational is such a high-falutin' name for fraction
<grin>. Personally, I don't think that rationals are any more
special-use than bignums. Also, one does not need to know about
rationals "What Every Computer Scientist Should Know About
Floating-Point Arithmetic". I'd provide a link for this, but you seem
to prefer cheap jibes without URLs for citations <grin>.
> * Lexical closures are interesting, but I'll let the other sub-thread
> discuss them. :^)
Sure... and google tells me that continuations have made their way
into c.l.t from time to time, too.
> * An object system. Yeah, we know. But why this insistance on
> "multiple dispatch with a metaobject protocol"? Multiple
> dispatch tends not to be done in the "OO in Tcl" community,
> since MD is a strongly-typed approach, and reading about MOPs in
> general at:
> http://www2.parc.com/csl/groups/sda/projects/mops/default.html
> I'd say that such methods of operating are quite reasonable in Tcl
> (yes, we like introspection!) They're just not given such a
> high-falutin' name like MetaObject Protocol. :^)
Well, I just mentioned MD and the MOP because this is what makes CLOS
unique (or nearly so) in the world of OO programming, AFAIK. But I
supposed I shouldn't have forgotten method combinations: standard,
before, after, around, and user defined method combinations. I only
know [incr Tcl] for OO Tcl extentions and, AFAIK, it does not define
its object system in terms of and/or with an object system as does
CLOS with the MOP. Perhaps other extensions already do (I'd be
interested in pointers to any, if they exist) or future extensions
will do so. But, like you mention, yeah, the Tcl community already
knows that it lacks a standard object system. If/When it decides on
one, only then will we see whether or not the system uses
introspection in its definition as fully as does the MOP.
But I don't know what you mean that MD can be ignored because it "is a
stronly-typed approach". It's really an issue of whether or not one
provides polymorphism on the type of one or more operands. If one has
typing strong enough to allow the OO system to select a method at
run-time based on the type of one operand, it would not need any more
strongly typed object to be able to select a method at run-time based
on the type of more than one operand. The "strength" of the typing
necessary for single dispatch is sufficient for multiple dispatch,
too. And unless I'm mistaken, even the Perl crowd has started talking
about adding MD. I know that a lot is changing with Perl 6, I don't
keep up with the latest-n-greatest, but I believe that Perl 6 will
have pretty much the same kind of anything will be implicitly
converted into anything else type system but blesses classes/instances
a bit differently that it has always had. Unless I'm missing
something, I don't see what's so different about Tcl in this regard...
> So, that's an "On it already", a "don't care", a "hot topic", and a
> "we've actually done that ages ago with extensions". :^D Indeed, it's
> quite possible to do acceptable OO in pure Tcl scripts.
Well, I wasn't attempting to create a list of features missing from
Tcl that could/should be added. I was just responding to a criticism
of Lisp being too big by starting off with a point that it provides
more features and then discussing that even though this is true, CLISP
doesn't really seem to be all that much "bigger" than "Tcl". I wasn't
trying to create an exhaustive list of Lisp features not in Tcl but
rather just a few that dropped off the top of my head.
What else might be in the list? I've already mentioned continuations,
related to closures but still different from them, e.g. call/cc in
Scheme, in this thread. _Practical Programming in Tcl and Tk_ talks
about if one "[wants] to create a whole procedure dynamically.
Unfortunately, this can be particularly awkward because a procedure
body is not a simple list." One can use format and regsub and all but
it's not nearly as nice as Common Lisp's defmacro, backquote,
macroexpand, etc, IMO. One can also acheive similar affects with
uplevel, too. But what would Tcl need to change <cough>syntax</cough>
to support something like Lisp's macros as well as Lisp does? And
code walkers? What about eader macros? We've already got a Tcl byte
code compiler. What about a "real" compiler; i.e. to native code?
Google also tells me that somebody seems to have written a Tcl
compiler, though many/most/all of those posts seem to have died out in
2001 (probably a kook?). Take all of this, roll it up into the Tcl
shell, you've got a corollary to Greenspun's 10th rule of programming,
and your journey to the dark side will be complete <grin>.
Ok, I'm awake! I've been watching this thread from the sidelines but
this caught my attention.
While not strictly related to closures, I've been working on an
implementation of a scope command that is very similar to what Donal
suggests. In fact, his example worked almost out of the box with my
code. The difference is slight; here's how it works in my world:
scope foo {
set c 0
proc count {} {
incr c
}
}
In our case the scopes are named (in practice, each file is given it's
own scope via a makefile, to mimic C's file scoping). Also, scoped
variables don't have to be declared, though we've created a 'private
var' command to make the code more self documenting (not shown in the
example above)
We've also implemented private procs, too. A more complete example:
scope foo {
private var c=0
private int count {} {
incr c
}
proc message {} {
puts "you've called this proc [count] times"
}
}
If you call count outside the scope you get an error, but inside the
scope it works as expected (reminds me of one of my favorite quotes:
"outside of a dog, a book is man's best friend. Inside of a dog it's too
dark to read (*)" . But I digress)
To answer Donal's question of how to implement that, in our case scope
isn't much more than a 'namespace eval' with some sugar coating. Most of
the magic happens with a custom 'proc' command that automatically
imports all known namespace variables.
In our implementation, the difference between scopes and namespaces is
that scopes don't nest. The other conceptual difference is that with
namespaces you have to explicitly export public commands whereas in our
scope command you explicitly declare private commands.
(*) Groucho Marx
[...]
>> I had that, I'd want everything that currently uses a script to
>> change, so I could use closures:
>> set c 0
>> button .c -command [lambda {event} {incr c}]
>> label .m -textvariable c
>> Oh, and I want some way to say that the c there is really the closure
>> c, not some random global variable called c. So I guess a bit more
>> syntax would be needed. Hmm, "label .m -textvariable :c"?
>
> Hmm, to me that says that we'd want to resolve variables against
> scopes-or-namespaces, and there are some complexities relating to how
> do you set a variable that doesn't currently have a locally-visible
> name. But that's the toughest issue. (As I said earlier, traces
> themselves should just work.) Clearly this would require new API, and
> it encourages a significant enough change in the way of working with
> scripts that I'd suggest that this ought to be a major-version-only
> change. (e.g. for Tcl 9.0 or Tcl 10.0).
OK, how about this (as a handwavey sort of syntax):
set env_1 [environment c]
set c 0
button .c -command [closure $env_1 [lambda {event} {incr c}]]
closure $env_1 [label .m -textvariable c]
I'm imagining that the environment command would create something very
like a dict, and that the named variables would be changed to refer to
values in that dict. And then the closure command would attach an
environment to a command (or proc, I guess) such that local variable
references would then use that environment (so "set c 0" would modify
something in that environment (but "set d 2" wouldn't)). (The intent
is that you'd store this environment in a local variable, and
attachments to things would increment the reference count, and things
going away would decrement the count, so it could vanish eventually in
the normal way.)
I like your syntactic way to do it; with my syntax it's not clear what
it would mean to do "set env_1 [environment c]; set env_2 [environment
c]". Also my "closure" syntax is definitely not transparent (I
couldn't decide whether I should use "closure $env_1 button ..." or
put the closure around the lambda, for example).
Maybe it might be clearer if I used "attach_environment" rather than
"closure"? Probably not, though, probably the idea itself is
confused.
[...]
> Well, I just mentioned MD and the MOP because this is what makes
> CLOS unique (or nearly so) in the world of OO programming, AFAIK.
I suspect you need typing to make multiple dispatch sane. In most OO
languages methods belong to classes (or objects) and you do things
like a.foo(42) and stuff, so it's syntactically clear that foo is a
method on a. But Lisp people didn't like such a big difference
between methods and functions (lisp has lots of higher-order
functions, so such a difference is costly), so they went for
polymorphic (generic) functions instead. So you define foo as
generic, and then define an implementation which works on arguments of
particular types, and then the types of any or all of the arguments
can be used for dispatch. (CLOS therefore can't have the same kind of
public/private/protected stuff that many OO languages have, because
these methods aren't members of particular classes.)
MOP strikes me as seriously optional, but then most accounts say that,
but add that on the rare occasions when it is needed, it's *really*
useful.
> But I supposed I shouldn't have forgotten method combinations:
> standard, before, after, around, and user defined method
> combinations. I only know [incr Tcl] for OO Tcl extentions and,
> AFAIK, it does not define its object system in terms of and/or with
> an object system as does CLOS with the MOP.
[incr Tcl] strikes me as a fairly direct attempt at putting something
like Java/C++ classes into Tcl, and it never looked right to me.
(Doubtless I'm missing a great deal of design effort that must have
gone into it to get it to that stage, of course!) XOTcl is probably
closer to what you want to look at. I don't think it provides a MOP,
but it provides mixins and things, which I think are equivalent to
before, after, etc. Or approximately so, anyway. I haven't really
played with it, but it seemed much more like I'd imagine an OO system
for Tcl.
[...]
> determined, and how would it be garbage collected when the
lambda/proc
> goes away?
I haven't worked out all the details yet. The namespace would have to
be named, and that name would have to figure in the string rep of the
proc (for lambdas), so it might be that you end up having to have the
environment being a fixed namespace somewhere. I don't think that's too
much of a problem. For named procs it's all much easier. As for cleanup
-- if the namespace is named, and possibly shared, you'd have to
manually delete it. This is no worse than the current situation.
>
>
> >
> >>
> >> When running the command with a local variable (which changes the
> >> value of that variable) you are, in effect, changing the command.
Each
> >> time the command is run, it does something different. In effect,
the
> >> command IS a different value each time you run it. As such, having
it
> >> change it's string rep/value makes sense to me. I understand it
may
> >> feel a bit "un-Tclish", but no more so that objects that change
their
> >> own state.
> >
> >
> > Values are not objects. Don't be fooled by the name Tcl_Obj -- it's
only
> > called that because Tcl_Value was already taken.
>
> I wasn't implying they were. I was just drawing an analogy to the use
of
> an object system in Tcl. When you call a setter for an object, you
are
> changing the "value" of the object you are dealing with
>
> $myObject setX 5
But the state which is changed here is hidden behind the object command
(which has an opaque name as its string rep). In the command example
you are directly manipulating a value.
> >> As for the part about it not being "how the dual rep works", can
you
> >> explain further? To me, it seems exactly like:
> >> set x 1 ;# here we have the string rep
> >> incr x ;# the string rep is no longer valid now
> >> puts "X: $x" ;# here the string rep is recreated for hte new
value
> >
> >
> > You are confusing values and variables, and also implementation
details
> > with the script-level semantics.
>
> I'm not confusing it, I'm just choosing to ignore it for this. Along
the
> same lines, I can see an object system where the value of the
variable
> the user has for the object IS the object.
>
> set myObject [createAnObject]
> puts $myObject
> ... the output is everything about the object ...
> $myObject setX 5
> puts $myObject
> ... this output is different than above ...
That can't possibly work in current Tcl without severely breaking
everything. The semantics of your language appear to be
pass-by-reference, with some automatic serialisation. I have no idea
how it would be implemented though.
> > As an example of why what you are proposing is not a good idea,
consider
> > the following:
> >
> > set scope {a 1 b 2}
> > set cmd1 [lambda {x} { set a $x } $scope]
> > $cmd1 12
> > puts "scope = $scope"
> >
> > The value stored in the scope variable is also used as the scope
for the
> > lambda (the static vars). With your modify-in-place semantics, this
code
> > would produce the result:
> >
> > scope = a 12 b 2
>
> I disagree. The output would be "a 1 b 2" as one would expect.
Modifying
> the lambda would change the lambda, not the values fed to it in its
> creation. When the lambda is first created, the refcount on $scope
would
> be incremented by one. When the lambda changes, a copy of the scope
> would need to be made, the same as if you did:
>
> set scope {a 1 b 2}
> set list1 [list {x} { set a $x } $scope]
> lset list1 2 {a 12}
Not the same at all -- the lset proc is given a *variable* name which
it can use to locate and update some mutable state. Your lambda proc
only has the argument pointers it is given -- it can copy them, but
then what? Where does it put the copies? When your lambda gets called
it is passed an array of pointers to its arguments. It is not passed a
pointer to "itself" because there is no such thing: the lambda *is* the
collection of values. It has no identity outside of this. The only way
to give it such an external identity (as in objects and variables) is
to give it a name. This isn't some obscure point of Tcl's semantics, or
me nit-picking, it's a fundamental property: *all* languages which have
mutable state have to hide it behind a name. Even a pointer in C is
just the name of an address in memory (that name is an integer).
Are these differences the result of intentional design, or a consequence
of your implementation? The nesting of scopes seems, to me, to be a
useful concept.
--
<URL: http://wiki.tcl.tk/> MP3 ID tag repair < http://www.fixtunes.com/?C=17038 >
Even if explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.
<URL: mailto:lvi...@gmail.com > <URL: http://www.purl.org/NET/lvirden/ >
I guess it depends on the arena one is using Tcl. I can't say
that I've needed to calculate using 'fractions' in my job in 20+ yrs.
Floating point, however, I do have to deal with, as well as large
integers.
: Perhaps other extensions already do (I'd be
:interested in pointers to any, if they exist) or future extensions
:will do so.
and select the words "Category Object Orientation" - that should
give you a look at a number of wiki pages that discuss OO. There are
a dozen or more of them that are unique extensions.
:But I don't know what you mean that MD can be ignored because it "is a
:stronly-typed approach". It's really an issue of whether or not one
:provides polymorphism on the type of one or more operands.
I think what he meant is that "on the type of one or more operands" ,
when Tcl passes arguments as strings only ... doesn't leave a lot of
need for _poly_ morphism if the only type is string...
:_Practical Programming in Tcl and Tk_ talks
:about if one "[wants] to create a whole procedure dynamically.
:Unfortunately, this can be particularly awkward because a procedure
:body is not a simple list." One can use format and regsub and all but
:it's not nearly as nice as Common Lisp's defmacro, backquote,
:macroexpand, etc, IMO. One can also acheive similar affects with
:uplevel, too. But what would Tcl need to change <cough>syntax</cough>
:to support something like Lisp's macros as well as Lisp does? And
:code walkers? What about eader macros?
I think that discussing the question of "where does a Tcl developer run into
awkward or missing features in Tcl" is a valuable exercise - particularly
when discussing real world applications, as opposed to what, in the past,
seemed to be an exercise in "what features from another language might
we add to Tcl".
[...]
> I think what he meant is that "on the type of one or more operands"
> , when Tcl passes arguments as strings only ... doesn't leave a lot
> of need for _poly_ morphism if the only type is string...
You didn't really mean to say that, did you?
[...]
The result of intentional design.
With nesting, if you see something like this:
scope foo {
proc bar {} {puts baz}
}
... you have to ask yourself, does baz belong in this scope, or some
other scope? If some other scope, which scope? Has the parent scope
changed?
Without nesting, it's either local or global as usual, or belongs to the
current scope. In our implementation scopes are presently only used at
the file level so when one encounters a scope variable they know it was
defined in the current file.
This, in our opinion, makes the code more readable.
Additionally, having nested scopes would have more of a performance
impact since name resolution could take longer given there may be a
longer trail of scopes to resolve.
By the way... when I say our scopes don't nest, that doesn't mean one
can't use one scope inside another. This is perfectly valid (though we
don't do it):
scope foo {
....
scope bar {
....
}
}
It's just that the name resolution inside bar doens't extend up into
foo. Each scope exists exactly one level below the global scope.
n...@cs.nott.ac.uk wrote:
> Robert Seeger wrote:
>>Fair enough. Along those lines, how is the "location" of the metadata
>>determined, and how would it be garbage collected when the
> lambda/proc goes away?
>
>
> I haven't worked out all the details yet. The namespace would have to
> be named, and that name would have to figure in the string rep of the
> proc (for lambdas), so it might be that you end up having to have the
> environment being a fixed namespace somewhere. I don't think that's too
> much of a problem. For named procs it's all much easier. As for cleanup
> -- if the namespace is named, and possibly shared, you'd have to
> manually delete it. This is no worse than the current situation.
You'd need to put some kind of name/pointer to the namespace in the
lambda, since you can't recreate it each time the lambda is called from
it's data. Personally, I hate the idea of the data for the proc locals
being stored in a normal namespace. In my opinion, you should have to go
through an introspection routine to access the locals for a proc that
you're not currently in.
As for being "no worse than the current situation", the problem is that
the current situation is, in my opinion, bad. The same problem exists
for most object systems at the moment... no garbage collection. I
believe automatic garbage collection should be a primary goal where
lambdas (and object systems) are concerned.
> But the state which is changed here is hidden behind the object command
> (which has an opaque name as its string rep). In the command example
> you are directly manipulating a value.
>>I'm not confusing it, I'm just choosing to ignore it for this. Along
>> the same lines, I can see an object system where the value of the
>> variable the user has for the object IS the object.
>>
>> set myObject [createAnObject]
>> puts $myObject
>> ... the output is everything about the object ...
>> $myObject setX 5
>> puts $myObject
>> ... this output is different than above ...
>
> That can't possibly work in current Tcl without severely breaking
> everything. The semantics of your language appear to be
> pass-by-reference, with some automatic serialisation. I have no idea
> how it would be implemented though.
There are some places it wouldn't work. Many places it would work. The
places it wouldn't work, however, are likely to be bad enough that it
can't be done in the general case. I'll concede that point.
Given the above, my concerns boil down to...
- A "pointer" to the data set for the lambda need to be stored in the
lambda value
- How does the data set get GC'd up when the lambda gets GC'd?
Rob Seeger
> "Miroslaw Osys" <omir...@poczta.onet.pl> writes:
>
> > Also - in old, good days Linux kernel could be configured with Tk
> > script, in newer versions they switched to Gtk and it makes only
> > more problems...
>
> I think that there should be more promotion of good GUIs with Tk.
>
> Tk makes GUI creation very easy, but creating a high quality GUI is
> tough.
>
> One of the things that have made Gnome and KDE such a success is that
> they promote usability standards, showing people the way to make
> usable programs.
>
> Some may say they aren't there 100% yet, but the important thing is
> that there are guidelines for refining and improving their work.
I was thinking on this and I am sure there is probably a book on Tcl/Tk
about making good UI's. The only book I have experience with is the
Welch (et al.) book and it is very good at the "core" of things. A book
dedicated (or maybe a wiki page/site) to creating professional level
UI's with Tcl/Tk would be a good thing.
Is there something along those lines already that can be built upon?
Robert
<snip>
> I think that discussing the question of "where does a Tcl developer run into
> awkward or missing features in Tcl" is a valuable exercise - particularly
> when discussing real world applications, as opposed to what, in the past,
> seemed to be an exercise in "what features from another language might
> we add to Tcl".
That would be an excellent discussion as well. There are folks that have
been using Tcl for a long long time and such an observance might
actually be a good discussion to have prior to Tcl 9.0.
Robert