Tcl: Difference between different ways of adding instance class - tcl

I found two ways to instance class:
one is:
class_name create instance
instance class_method
the other is:
set instance [class_name new]
$instance class_method
Each way worked well, so is there any difference between two ways?

The only difference is that the new method on classes generates a unique name for you, and with the create method you get to specify what the name is. Both are provided because there's use cases for each of them. Use whichever makes sense for you. (Note that class objects themselves are always named because of how they're generally used, and so you can't normally create classes with new; it's hidden on oo::class instances.)
For the sake of completeness only, there's an additional way to make instances, createWithNamespace, which lets you also specify the name of the state namespace of the object. It's not exposed by default (you have to manually export it for general use) and is pretty specialist for people who aren't doing deep shenanigans. You probably don't want to use it.
At some point in the future new may be enhanced so that it also enables garbage collection for the object, whereas create will not (because you know the name out-of-band). Specifically, I've written a TIP for doing this for Tcl 9.0 but I don't have a working implementation yet, so do not hold your breath.

I have tried some cases,
class defined as this:
oo::class create class_name {
variable text
method add {t} { set text $t }
method print { } { puts $text }
}
First way:
class_name create foo
foo add "abc"
foo print
class_name create foo
It will return:
abc
Error: can't create object "foo": command already exists with that name
Second way:
set foo [class_name new]
$foo add "abc"
$foo print
set foo [class_name new]
$foo add "def"
$foo print
It will return:
abc
def
This is one difference I found, and it seems like secnod way is more convenient.
Still expecting master to provide authoritative answers or documents.

Related

Perl6: Convert Match object to JSON-serializable Hash

I am currently gettin' my hands dirty on some Perl6. Specifically I am trying to write a Fortran parser based on grammars (the Fortran::Grammar module)
For testing purposes, I would like to have the possiblity to convert a Match object into a JSON-serializable Hash.
Googling / official Perl6 documentation didn't help. My apologies if I overlooked something.
My attempts so far:
I know that one can convert a Match $m to a Hash via $m.hash. But this keeps nested Match objects.
Since this just has to be solvable via recursion, I tried but gave up in favor of asking first for the existance of a simpler/existing solution here
Dealing with Match objects' contents is obviously best accomplished via make/made. I would love to have a super simple Actions object to hand to .parse with a default method for all matches that basically just does a make $/.hash or something the like. I just have no idea on how to specify a default method.
Here's an action class method from one of my Perl 6 projects, which does what you describe.
It does almost the same as what Christoph posted, but is written more verbosely (and I've added copious amounts of comments to make it easier to understand):
#| Fallback action method that produces a Hash tree from named captures.
method FALLBACK ($name, $/) {
# Unless an embedded { } block in the grammar already called make()...
unless $/.made.defined {
# If the Match has named captures, produce a hash with one entry
# per capture:
if $/.hash -> %captures {
make hash do for %captures.kv -> $k, $v {
# The key of the hash entry is the capture's name.
$k => $v ~~ Array
# If the capture was repeated by a quantifier, the
# value becomes a list of what each repetition of the
# sub-rule produced:
?? $v.map(*.made).cache
# If the capture wasn't quantified, the value becomes
# what the sub-rule produced:
!! $v.made
}
}
# If the Match has no named captures, produce the string it matched:
else { make ~$/ }
}
}
Notes:
This totally ignores positional captures (i.e. those made with ( ) inside the grammar) - only named captures (e.g. <foo> or <foo=bar>) are used to build the Hash tree. It could be amended to handle them too, depending on what you want to do with them. Keep in mind that:
$/.hash gives the named captures, as a Map.
$/.list gives the positional captures, as a List.
$/.caps (or $/.pairs) gives both the named and positional captures, as a sequence of name=>submatch and/or index=>submatch pairs.
It allows you to override the AST generation for specific rules, either by adding a { make ... } block inside the rule in the grammar (assuming that you never intentionally want to make an undefined value), or by adding a method with the rule's name to the action class.
I just have no idea on how to specify a default method.
The method name FALLBACK is reserved for this purpose.
Adding something like this
method FALLBACK($name, $/) {
make $/.pairs.map(-> (:key($k), :value($v)) {
$k => $v ~~ Match ?? $v.made !! $v>>.made
}).hash || ~$/;
}
to your actions class should work.
For each named rule without an explicit action method, it will make either a hash containing its subrules (either named ones or positional captures), or if the rule is 'atomic' and has no such subrules the matching string.

create vs new in object creation with TclOO

I am looking into TclOO and found that we can use create or new for object creation.
With create we can provide customized name, whereas with new, it is computer generated.
#!/bin/bash
#\
exec tclsh8.6 $0 $#
::oo::class create calc {
method add { a b } {
return [ expr {$a+$b} ]
}
}
calc create c;
set t [ calc new ]
# Both serves the same purpose here
puts [ c add 3 4 ]
puts [ $t add 1 2 ]
Is it only about object naming convenience provided by compiler to developer? Also, what can be the scenario where I should prefer new instead of create ?
The only difference between new and create is exactly that create lets you give the name to use, and new makes a fresh one for you. Some kinds of objects (specifically classes) conceal their new method so that you are strongly encouraged to only make named instances — given how people use classes, that makes a lot of sense in practice — but it's just concealment and you can override that if you want:
oo::define oo::class {
export new
}
Use new when you don't care about the name and just want it to be different from everything else. That's the basic rule-of-thumb.
Singletons
It's also perfect to use create to make instances where other OO systems might use singletons. What's more unique than giving them a nice unique name that you control.
Lifespan management
The main scenario I'm aware of otherwise where using create is much more useful is when you want to bound the lifespan of an object to another. In that case, creating the object within the context of another will put the object handle in the container's namespace, and will automatically trigger a delete on the deletion of the container:
oo::class create InsideThing {
constructor {} {
puts "Made a [self] that is an InsideThing"
}
destructor {
puts "Deleted a [self] that is an InsideThing"
}
}
oo::class create Container {
constructor {} {
puts "Created a [self] that is a Container"
InsideThing create inner1
InsideThing create inner2
}
destructor {
puts "Deleted a [self] that is a Container"
}
}
set c [Container new]
puts "Do stuff..."
$c destroy
If you run that code, you get this:
Created a ::oo::Obj13 that is a Container
Made a ::oo::Obj13::inner1 that is an InsideThing
Made a ::oo::Obj13::inner2 that is an InsideThing
Do stuff...
Deleted a ::oo::Obj13 that is a Container
Deleted a ::oo::Obj13::inner1 that is an InsideThing
Deleted a ::oo::Obj13::inner2 that is an InsideThing
This technique is widely used in TDBC to manage result sets and statements within the context of their containers (statements and connections, respectively).
Tk Megawidgets
Finally, if you are creating Tk megawidgets using TclOO, you will definitely be using create but in overridden form, as Tk widgets must have unqualified names that start with a . and which have a specific embodied hierarchy, and megawidgets are very strongly advised to follow the same pattern. Because the name matters a lot in this situation, new is the wrong method; it is better to start from create. Tk 8.6 has some support classes (not publicly described, but used internally for some dialogs) that makes this sort of thing easier: tk::Megawidget and tk::MegawidgetClass. Check out megawidget.tcl in a Tk 8.6 distribution for details.

How can I declare an adjustable number of variables in the constructor of TclOO object which are visible in other methods?

This is somewhat similar to this question TclOO Variable Scope with Inheritance/superclass,
but I would like to make a number of variables available to the methods in my class, and which ones and how many those are is determined dynamically at runtime.
Pseudocode:
oo::class create myClass {
constructor {registryObject} {
foreach variable [$registryObject getVariables] {
<make $variable visible to all methods>
}
method useVariable{} {
<do something with previously declared variable>
}
}
is there any way to achieve this without another layer of indirection? The problem is that the registryObject gives me a variable number of references to other objects, which I would like to be accessible in derived classes. As with the question referenced above, the point here is brevity in the code of a derived class. I am willing to use dirty and complicated hacks in the superclass if neccessary.
It's going to be really hard to do exactly what you're asking for.
The variable class declaration used in TclOO adjusts how Tcl resolves variables in (normal class-defined) methods declared in the same class. It does not affect methods defined by superclasses, subclasses or instances (which should use their own variable declarations if they want them). The variables themselves are visible though — all variables associated with an instance are variables in the current namespace while the object is executing (which is instance-specific) — they just have to be brought in explicitly.
If the response to the registry object query was being used to dynamically create instance methods, you could do it:
constructor {registryObject} {
foreach variable [$registryObject getVariables] {
oo::objdefine [self] variable $variable
}
oo::objdefine [self] method foo {...} {
# This method *will* have automatic access
}
}
Yes, the method is created inside the constructor (and it doesn't make much sense to create the methods on the class). No, most other object systems don't have instance methods. Think of it as being a bit like giving each instance its own special little class. (That's not what happens, but it's not too far off.)
What you were asking to do
If you were going to create the methods for all instances of the class, it makes sense to do so either during the creation of the class (boring, conventional!) or during the creation of the first instance of the class, assuming the registry does the sensible thing and gives the same answer for all instances of that class. If that's the case, you could do it. Overriding the create and new methods is probably the simplest way to do it:
oo::class create myClass {
self method create {name registryObject} {
foreach var [$registryObject getVariables] {
oo::define [self] variable $var
}
next $name $registryObject
}
self method new {registryObject} {
foreach var [$registryObject getVariables] {
oo::define [self] variable $var
}
next $registryObject
}
constructor ...
method ...
}
It's a bit slow to do it this way because each object creation will trigger recompilation of lots of things, but it ought to work.
Or you could do it in the constructor; I think it's less elegant but you might disagree:
constructor {registryObject} {
oo::define [self class] variable {*}[$registryObject getVariables]
}
I think both of these options are not good ways to do it and I recommend doing the registry lookup during class creation. Or making the methods not rely on the variable mechanism, instead using my variable (very much like global but for instance variables) to access the variables they need.
If only I could figure out more exactly what the bigger picture was, I could help you better…

Manipulation of variables inside a proc with bind

Sorry for the title couldn't think of anything better.
Heres my question:
Im trying to change a variable inside a proc only when the user hits space. the proc loops itself with after so if the user wish to hit space more then once the variable will increment.
Heres what I know:
There are many ways to go about this. You can pass variable inside the proc, you can link the variable with global or upvar and/or if your in a namespace then you can use variable. but the only one that seem to work with my is global. I'm getting a feeling it's because global makes a link but if thats true then variable should work too, right?
Here my test code:
proc test1 {} {
global testing
bind . <Key-a> {incr testing}
puts $testing
puts "test2"
after 100 test2
}
namespace eval test2 {
variable testing 0
namespace export {[a-z]*}
proc of1 {} {
variable testing
bind . <Key-a> {incr testing}
puts $testing
after 100 test3::of1
}
}
proc test3 {testing} {
bind . <Key-a> {incr testing}
puts $testing
puts "test4"
after 100 test4 $testing
}
set testing 0
#test1
test2::of1
#test3 0
grid .c
side question:
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
Variables in Callbacks
The scripts registered by the bind command — also in things like after events and fileevent callbacks — are evaluated in the global scope because they may be called long after the procedure that defined them returns; Tcl does not do scope capture (and that's actually a really complicated feature, so it isn't likely to come soon unless someone writes lots of code). This means that the variable that you want your procedure to notice changes to must also have global scope.
However, namespaced variables count just fine as global variables for the purpose of this discussion as they're nameable from a global context (real local variables are not). That means that we can do several ways to build a script that accesses a namespace variable from a bind-defined callback. Here's one of the nicer ones:
bind . <Key-a> [namespace code {incr testing}]
That's effectively the same as this:
bind . <Key-a> [list namespace eval [namespace current] {incr testing}]
(There are some strict differences that don't matter in this example.)
Another way to do the callback is this:
bind . <Key-a> [list incr [namespace which -variable testing]]
Which in this case is going to be much like:
bind . <Key-a> [list incr [namespace current]::testing]
If things are getting any more complicated than this toy example, it's time to stop updating variables directly in a binding script and instead write a helper procedure. That always simplifies things a lot. Or to use a class/object to encapsulate the details.
The variable Command: Why and Where to Use It
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
That's a good question. In fact, what global does is very much like upvar #0 (with the variable names doubled up), and set is a fundamental variable access command. They're commands that you can use regularly wherever you want their behaviour.
The variable command is rather stranger. What it does is three-fold:
If called in a namespace context and the variable does not exist in that namespace, it creates that variable in a present-but-unset state.
If called in a context with local variables, it links a local variable with the name (after stripping everything up to the last namespace separator) to a namespace variable with the name (using the whole supplied name if there are qualifiers, and resolving non-absolute names with respect to the current context namespace). This also forces the namespace variable to exist in the present-but-unset state.
If a value is given, the namespace variable is set to the value. This gets rid of the present-but-unset-ness.
The important behaviour is actually the creating of that present-but-unset state, since otherwise you can end up with a set (or array set) in the namespace escaping that namespace and instead working with a global variable, but not always. It all depends on the exact behaviour of the code that resolves variables, and that's deeply tricky. This is hard to explain properly, and ridiculously hard to justify. It's been the cause of quite a few outright bugs, and is definitely no better than a horrible misfeature.
The setting of the initial value is little more than a lollipop; you could instead put set straight afterwards without ill-effect. What's more, it inhibits using variable to pull in multiple variables from a namespace except in the case where you're setting them to a known value; great for initialisation, but sucky for other uses. (In case you hadn't guessed, I think this is an area of Tcl where the interface was got rather badly wrong back when it was introduced, back in Tcl 8.0. Not one bit of this is nice.)
The key take-away is this though:
Always use variable in your namespaces to declare variables as this is the only way you can be sure that the semantics is predictable. You can then initialise them any way you want. (You have to do it this way if you're creating arrays.)
Fully-qualified variable names have no craziness associated with them. Tcl always knows exactly how to look up the thing you're naming in that case.

Adding help to the user defined functions in TCL

how can i add some kind of help to the user defined functions in TCL
Supposing if i have a function called runtest {ip_address test_time},
How to describe what the test or procedure is about in the TCL_shell?
How can i specify the information to the user, if he types in function_name --help in the TCL shell the user should be able to know what the function does and what exactly are the parameters.
how can i do this?
While it is true that it is not part of the language, it is fairly easy to implement something that adds this functionality to pre-existing functions. The only caveat being that the function then cannot take the string --help as a valid argument since that argument will trigger the feature.
Here's one simple implementation:
# Lets call the feature "document". As in, add documentation:
proc document {procname text} {
rename $procname __$procname
proc $procname args [string map [list %TEXT% $text %PROC% $procname] {
if {$args == "--help"} {
puts {%TEXT%}
} else {
set script [linsert $args 0 __%PROC%]
return [uplevel 1 $script]
}
}]
}
What this does is to override the function (by renaming and then declaring another function of the same name) and see if the function is called with the argument --help. If it is it prints the documentation otherwise it executes the original function. Just be careful not to call this twice on the same function (it can be modified for it to work though).
So you can do things like:
proc foo {} {puts 2}
document foo {Prints the number 2.}
Now if you call:
foo --help
and it would output:
Prints the number 2.
You don't have to touch the existing procedures:
proc help {procname} {
puts $::helptext($procname)
}
proc addhelp {procname text} {
set ::helptext($procname) $text
}
addhelp foo "this is the help text for procedure foo"
help foo
Without redefining the proc command, you cannot. Ie, that functionality is not built into the language, but would be possible to add if you so wished.
I will note that adding the capability yourself, while possible, is probably beyond the difficulty where it's worth doing, especially for someone that isn't intimately familiar with the language. That being said, you can check the tclers wiki for a preexisting implementation.
I tend to prefer to put help in a separate file (e.g., as HTML) which lets me browse it in another window. There are so many ways you can do that while still keeping the docs with the code, such as through doxygen.
There are also a number of ways of doing interactive documentation, several of which are described in the Tcler's Wiki; it would seem to me that some of the techniques mentioned on that page (together with #slebetman's answer) would give you what you wanted. (I think it would be easier to have a separate help command as that would avoid having the help syntax interfere with the command, but that's your call.)