It looks to me like the package stooop is
redefining the commands [::proc]! Yuck!
It seems like a better idea to me for stooop
to define and export [::stooop::proc]. Then
if I want to import it and make use of it as
a replacement for [proc] in my namespace, or
for a replacement for [::proc] more globally,
I have that choice. Currently the stooop
package makes the choice for you.
Logged In: YES
user_id=80530
stooop also defines [::_proc] ( the renamed [::proc] ).
Packages in tcllib are not supposed to create commands
outside their namespaces.
Logged In: NO
As English is not my native language, please define what you mean by Yuck!
Would you also please include a short script which demonstrates the bug?
Thanks.
Jean-Luc
Logged In: YES
user_id=80530
The README for tcllib says:
...
There are some base requirements that a module must meet
before it
will be added to tcllib:
* the module must be a proper Tcl package
* the module must use a namespace for its commands and
variables
* the name of the package must be the same as the name of
the
namespace
...
This means that the package "stooop" should only
define commands that match ::stooop::* .
However, the package stooop is currently
defining a command named ::proc and a command
named ::_proc , against the tcllib policy.
"Yuck!" means "I'm tasting something bad."
Logged In: NO
Stooop needs to overload the proc command in order to make such code transparent:
proc memberProcedure {this ..} {...
virtual proc virtualProcedure {this ..} {...
...
Now Consider the following package:
package provide n 1.0
namespace eval n {
proc proc {name arguments args} {
uplevel 1 ::_proc [list $name $arguments] $args
}
namespace export proc
}
and the following user code:
package require n
rename proc _proc
namespace import n::*
proc p {} {}
Note that in order to use the n package, the user needs to rename proc to the predefined _proc (otherwise n::proc
will fail), and in the correct order, before importing n exported procedures.
Do you have anything better, simpler than the above (and backward compatible)?
Jean-Luc
Logged In: YES
user_id=80530
Here's the right approach:
package provide stooop $version
namespace eval stooop {
namespace export proc
::proc proc {name arguments args} {
# Use fully qualified name to get Tcl's
# built-in [proc].
uplevel 1 ::proc [list $name $arguments] $args
}
}
And the user code:
namespace eval user {
package require stooop
namespace import ::stooop::proc
proc p {} {}
}
It is true that you will not be able to
[namespace import] the command [::stooop::proc]
into the global namespace. Depending on your
perspective, you can attribute this to:
1) stooop's choice of re-using a command name
already used by Tcl.
2) Tcl's choice to put built-in commands directly
in the global namespace rather than ::tcl.
Code in the global namespace would be able to use
the fully qualified [::stooop::proc] to distinguish
from [::proc].
I'm not a stooop user or developer, so it's not clear
to me what you accomplish by overwriting [::proc] and
whether the alternative above is compatible. However,
what you are currently doing is incompatible with tcllib.
Logged In: NO
dgp wrote:
> It is true that you will not be able to [namespace import] the command [::stooop::proc] into the global
namespace. Depending on your perspective, you can attribute this to:
1) stooop's choice of re-using a command name already used by Tcl.
2) Tcl's choice to put built-in commands directly in the global namespace rather than ::tcl.
> Code in the global namespace would be able to use the fully qualified [::stooop::proc] to distinguish
from [::proc].
Thank you for the explanations. I understand well now.
> I'm not a stooop user or developer, so it's not clear to me what you accomplish by overwriting [::proc]
That is required in order, for example, to detect member procedures (this as first argument), constructors
(procedure name = class name), setup internal namespace variables, ...
> and whether the alternative above is compatible.
I am sure that it is incompatible with most if not all of the stooop code out there.
> However, what you are currently doing is incompatible with tcllib.
Well, I could internally use a stooop::proc instead of a proc, but the rename I am afraid cannot be removed.
So, do you want to remove stooop from tcllib? Should I (can I) do it myself?
Jean-Luc
Logged In: YES
user_id=80530
Before you do anything that severe, I'll assign this over
to Andreas for another opinion. Maybe he will see a way
to resolve the problem.
The real problem is that stooop apparently needs to "wrap"
a command provided by another package (Tcl, in this case)
and the facilities for doing this with [rename] between
namespaces do not work well. We were just chatting about
this a few days ago.
I think offering an alternative for import is more modular than
direct wrapping, but perhaps you've got a case where direct
wrapping is required. I haven't looked into the stooop code
in detail.
To help clarify matters, what are the compatibility requirements
for stooop users? You suggeted that there must continue to
be a command named [::_proc], but why? Isn't that an "internal"
detail of stooop, on which users do not have to rely?
Logged In: NO
Don Porter (dgp) wrote:
>To help clarify matters, what are the compatibility
requirements for stooop users? You suggeted that there must
continue to be a command named [::_proc], but why? Isn't
that an "internal" detail of stooop, on which users do not
have to rely?
The requirements are simply that for example the following
code keeps working:
class c {
proc c {this p} {
set ($this,m) $p
}
proc s {} {}
}
proc c::~c {this} {}
virtual proc c::v {this} {}
delete [new c 0]
That requires, according to our previous postings, renaming
the proc command (I rename it to ::_proc, but it could be
any name, so users do not need to know that particular name)
so that the stooop::proc can take over and detect whether
the procedure name is "c" (the constructor, in the "c"
namespace), "~c" (the destructor), that a procedure first
argument is "this" (normal member procedure) or not "this"
(static member procedure). Also a "variable {}" instruction
is added at the beginning of a member procedure so that it
is possible to store member data in the empty named
namespace array using the "($this,...)" conventionnal
construct.
Note that virtual member procedures are implemented using
"stooop::virtual" which takes the "proc" string as first
argument for visual compatibility with the other member
fonctions.
I hope that this summary helps you.
I will be happy to provide further details if needed.
Thank you for your help.
Jean-Luc
Logged In: NO
To complement myself:
> That requires, according to our previous postings, renaming the proc command (I rename it to ::_proc, but it
could be any name, so users do not need to know that particular name) so that the stooop::proc can take over...
The thing is that proc needs to be overriden and the only way to do that so that it works in the global namespace is
to rename ::proc (at least that is the only way I know...).
Also, stooop::proc obviously needs to invoke the original ::proc for procedures that do not belong to a stooop class.
Jean-Luc
Logged In: YES
user_id=75003
Here is a log from discussion on the Tcler's chat about this.
aku If I understand JF explanations correctly at least
_proc can be moved into the stooop namespace.
dgp Yes, fortunately, Tcl's [:roc] does not care what
namespace it lives in.
aku ... Off the top of my head I see now way around
the redefinition of :roc to accomplish what he wants, i.e. use
of a unadorned [proc] inside of stoop classes to define
member commands.
dgp Inside of stooop classes ought to be no problem,
right? It's outside of them in the global namespace where we
have trouble.
aku I retract my opinion about the renamed proc aka
_proc a bit. stooop does not know if the command it renames
is the builtin proc, or a different command/procedure from
another package. If the latter is the case it might require to be
in the global namespace
dgp Except for the global namespace, this is why
importing by the user is better than wrapping by the provider.
aku I don't know how the [class] command is
implemented. I deduce from his explanations that [class] sets
up some namespace and or internal variables and then
executes the class definition script in the global namespace.
So [proc] in the class definition is executed in :: . If the class
def script were executed inside of ::stooop it would use the
internal stooop:roc
aku Back to _proc - Because of what I said the safest
way for stooop might indeed be to leave _proc in ::, as bad as
that is
dgp OK, maybe, but that could be changed. My point
is that stooop is in control at that point and could take care of
the problem.
aku I guess so. We have to source-dive.
aku Anotrher approach could be to use a different
interp, specially setup to interp class definitions. That way
only the class commands are possible. It also disables fun
stuff like loops to generate a series of similar member
commands
Logged In: YES
user_id=80530
more chat:
aku Oh, wait. His example explictly has member commands
defined outside of the class definition script.
aku For that he definitely either has to overload [proc], or
use a qualified name [stoop:roc]. The latter breaks existing
code
dgp Or his users have to code in their own namespace.
dgp But, yes, it's a compatibility problem with his current
user base.
dgp I could imagine having an additional command in stooop
that set up the new [::proc] definition.
dgp Then for compatibility, current stooop users would have
to call that command to support their direct call to
[::proc].
dgp But stooop itself would not force that definition on
everyone.
aku That might be best. Have the user make explicit that
they use this feature. Still requires them to change their
code, but not as much as replaced all proc's with
stooop::proc's
Logged In: NO
Thanks for enlightnening discussions, which led me to the following solution proposal:
namespace eval stooop {
namespace export class new delete
variable proc ::proc
::proc proc {name arguments args} {
variable proc
# invoke the real Tcl proc command:
uplevel 1 $proc [list $name $arguments] $args
# unless it is a class procedure:
# ... stooop specifc code here ...
}
::proc import {args} {
variable proc
set proc stooopRenamedProc
namespace export proc
uplevel 1 "
catch {rename proc $proc}
foreach pattern $args {
namespace import stooop::\$pattern
}
catch {namespace import stooop::proc}
"
}
::proc class {args} {}
::proc new {classOrId args} {}
::proc delete {args} {}
}
Then the user can optionally use:
stooop::import *
instead of the former:
namespace import stooop::*
Please let me know what you think.
Thanks, Jean-Luc
Logged In: YES
user_id=80530
Thoughts:
1. I wish this had been ready to be in tcllib-1.3
2. The idea seems good.
3. The implementation needs some corrections. ([uplevel] between
namespaces is always tricky.)
The export of stooop::proc appears to be conditional on calling
[stooop::import]. Is that right? Is [stooop::proc] part of
the package
interface or not?
Our previous analysis indicated that import into the global
namespace
needed special treatment. I don't think I see that here.
Perhaps a sketch of the documentation that would accompany this
change will help me follow it.
Logged In: NO
>1. I wish this had been ready to be in tcllib-1.3
I wish you had told me as I was not aware 1.3 was about to be released.
>3. The implementation needs some corrections. ([uplevel] between namespaces is always tricky.)
Please suggest some corrections. Unfortunately, I cannot do any better than what I proposed...
> The export of stooop::proc appears to be conditional on calling [stooop::import]. Is that right? Is [stooop::proc]
part of the package interface or not?
Since the Tcl proc command was renamed, a new proc is needed to replace it. That is what the "namespace
export proc" and the "namespace import stooop::proc" do in stooop::import{}. Maybe there is a better way to
achieve that effect?
>Our previous analysis indicated that import into the global namespace needed special treatment. I don't think I
see that here.
I do not understant the above. What I understood is that the renaming of proc should not be forced on the user, and
that is what is achieved by the optional use of stooop::import. If the user chooses not to invoke stooop::import,
then he has to explicitely use stooop::proc in his code.
>Perhaps a sketch of the documentation that would accompany this change will help me follow it.
I was waiting for the change to be accepted before writing the documentation, but it is quite simple:
- if the user does not use stooop::import, then he has to use the qualified stooop procedures, as in:
stooop::class c {
stooop::proc c {this} {}
stooop::proc ~c {this} {}
}
stooop::proc c::p {this m} {}
stooop::delete [stooop::new c]
If he uses:
stooop::import *
then the compatibility with the existing stooop code is maintained (the sample code in one of my previous
messages should work in that case).
Jean-Luc
Logged In: YES
user_id=80530
hmmm... there I go assuming context again... sorry.
A subscription to the tcllib-devel@lists.sf.net mailing list
would
be a good idea.
I intend to propose revisions, that's why I needed documentation
of intent. Your comments indicate that you do not want or plan
for stooop users to use [namespace import] at all. I think
that's
a mistake. [namespace import] is the general mechanism, and
people are going to use it. Offering [stooop::import] in
addition
is a good idea if it helps work around the global [::proc]
problem,
but [namespace import] should still work, and the docs should be
clear about what commands are exported as stooop's public
interface.
Since we can't make tcllib-1.3 anyway, and other Tcl 8.4-related
things have quicker deadlines right now, I'll get back to
this later.
Logged In: YES
user_id=80530
if you like, you can assign this report to me, then I'll be sure
not to forget about it.
Logged In: NO
> A subscription to the tcllib-devel@lists.sf.net mailing list would be a good idea.
I would like it, but unfortunately, I have a very bad Internet connection from home and I am not allowed to do that at
work...
> I intend to propose revisions, that's why I needed documentation of intent. Your comments indicate that you do
not want or plan for stooop users to use [namespace import] at all.
That is incorrect. As a matter of fact, that is how the current version of stooop works. The problem that I overlooked
is that it also renames proc no matter what, so that the stooop's proc always kicks in, which is needed for stooop
to work at all, as we have seen in the previous postings.
> I think that's a mistake. [namespace import] is the general mechanism, and people are going to use it. Offering
[stooop::import] in addition is a good idea if it helps work around the global [::proc] problem, but [namespace
import] should still work, and the docs should be clear about what commands are exported as stooop's
public interface.
At this time, I believe that it is clear what stooop exports (both in the documentation examples and in the source
file with the "namespace export" line).
Now for the proc problem (assuming the stooop code is rewritten based on my latest proposal a couple of
messages ago):
- if the user does not want to import anything, then he has to use stooop::proc and other fully qualified stooop
commands, in which case stooop is a well behaved package
- the user either has existing stooop code, or wants to use class, new, delete, ... and other commands directly. As
we have seen in the previous postings, that requires the modified stooop proc to replace the Tcl proc command at
the global level. That is why I proposed the "stooop::import *" instruction which does just that and that can be used
as a replacement for the currently used "namespace import stooop::*". This way, the only thing that all the current
stooop users have to do is to modify a single line of code to use the new and improved stooop.
I think that it is a good compromise, provided that change and the effects are properly documented, which is the
first thing I would do once we reach an agreement.
Jean-Luc
Logged In: YES
user_id=80530
will look this over again, to see if
I want to propose any changes
for tcllib 1.4 release.