It's hard for me to accept OOP because as hard as I try I really can't
not think that OOP is the Emperor's new clothes.
Example from snit:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
dog spot
puts [spot tail wag]
puts [spot tail droop]
Why exactly is that any better than this:
proc dog {args} {
if {$args == "tail wag"} {return "Wag, wag"}
if {$args == "tail droop"} {return "Droop, droop"}
}
puts [dog tail wag]
puts [dog tail droop]
If you only *ever* have one dog, either are essentially the same.
But, if at some point you want to manage spot, frank, sam, donut, and
alexa, all of which are different dogs, then you can do:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
Once to create a generic "dog" framework.
And then later do, all in the same single program instance:
dog spot
dog frank
dog sam
dog donut
dog alexa
And have five, fully independent dogs (each with their own independent >"data") that you can manipulate, without having to do anything special
at the time you create them.
I.e., you can wag spot's tail, give food to sam, and label alexa with
"last vet visit was 2024-06-21" (assuming you had mentods for 'feed'
and for 'last-vet-visit' in the generic framework.
It's hard for me to accept OOP because as hard as I try I really can't
not think that OOP is the Emperor's new clothes.
Example from snit:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
dog spot
puts [spot tail wag]
puts [spot tail droop]
Why exactly is that any better than this:
proc dog {args} {
if {$args == "tail wag"} {return "Wag, wag"}
if {$args == "tail droop"} {return "Droop, droop"}
}
puts [dog tail wag]
puts [dog tail droop]
On Tue, 25 Jun 2024 22:29:46 -0000 (UTC), Rich wrote:
If you only *ever* have one dog, either are essentially the same.
But, if at some point you want to manage spot, frank, sam, donut, and >alexa, all of which are different dogs, then you can do:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
Once to create a generic "dog" framework.
And then later do, all in the same single program instance:
dog spot
dog frank
dog sam
dog donut
dog alexa
And have five, fully independent dogs (each with their own independent >"data") that you can manipulate, without having to do anything special
at the time you create them.
I.e., you can wag spot's tail, give food to sam, and label alexa with
"last vet visit was 2024-06-21" (assuming you had mentods for 'feed'
and for 'last-vet-visit' in the generic framework.
And how is that better than this:
proc dog {dogname args} {
if {$args == "tail wag"} {return "$dogname Wag, wag"}
if {$args == "tail droop"} {return "$dogname Droop, droop"}
if {$args == "eats food"} {return "$dogname Nom, nom"}
if {$args == "goes to vet"} {return "$dogname Tries to run"}
}
puts [dog fido tail wag]
puts [dog lucky tail droop]
puts [dog charlie eats food]
puts [dog mitts goes to vet]
That will handle multiple dogs pretty well.
One "method," one "if." It's pretty much it, isn't it?
On Tue, 25 Jun 2024 22:29:46 -0000 (UTC), Rich wrote:
If you only *ever* have one dog, either are essentially the same.
But, if at some point you want to manage spot, frank, sam, donut, and >>alexa, all of which are different dogs, then you can do:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
Once to create a generic "dog" framework.
And then later do, all in the same single program instance:
dog spot
dog frank
dog sam
dog donut
dog alexa
And have five, fully independent dogs (each with their own independent >>"data") that you can manipulate, without having to do anything special
at the time you create them.
I.e., you can wag spot's tail, give food to sam, and label alexa with
"last vet visit was 2024-06-21" (assuming you had mentods for 'feed'
and for 'last-vet-visit' in the generic framework.
And how is that better than this:
proc dog {dogname args} {
if {$args == "tail wag"} {return "$dogname Wag, wag"}
if {$args == "tail droop"} {return "$dogname Droop, droop"}
if {$args == "eats food"} {return "$dogname Nom, nom"}
if {$args == "goes to vet"} {return "$dogname Tries to run"}
}
puts [dog fido tail wag]
puts [dog lucky tail droop]
puts [dog charlie eats food]
puts [dog mitts goes to vet]
That will handle multiple dogs pretty well.
One "method," one "if." It's pretty much it, isn't it?
On Tue, 25 Jun 2024 22:29:46 -0000 (UTC), Rich wrote:
If you only *ever* have one dog, either are essentially the same.
But, if at some point you want to manage spot, frank, sam, donut, and >>alexa, all of which are different dogs, then you can do:
snit::type dog {
method {tail wag} {} {return "Wag, wag"}
method {tail droop} {} {return "Droop, droop"}
}
Once to create a generic "dog" framework.
And then later do, all in the same single program instance:
dog spot
dog frank
dog sam
dog donut
dog alexa
And have five, fully independent dogs (each with their own independent >>"data") that you can manipulate, without having to do anything special
at the time you create them.
I.e., you can wag spot's tail, give food to sam, and label alexa with
"last vet visit was 2024-06-21" (assuming you had mentods for 'feed'
and for 'last-vet-visit' in the generic framework.
And how is that better than this:
proc dog {dogname args} {
if {$args == "tail wag"} {return "$dogname Wag, wag"}
if {$args == "tail droop"} {return "$dogname Droop, droop"}
if {$args == "eats food"} {return "$dogname Nom, nom"}
if {$args == "goes to vet"} {return "$dogname Tries to run"}
}
puts [dog fido tail wag]
puts [dog lucky tail droop]
puts [dog charlie eats food]
puts [dog mitts goes to vet]
That will handle multiple dogs pretty well.
One "method," one "if." It's pretty much it, isn't it?
And how is that better than this:
proc dog {dogname args} {
if {$args == "tail wag"} {return "$dogname Wag, wag"}
if {$args == "tail droop"} {return "$dogname Droop, droop"}
if {$args == "eats food"} {return "$dogname Nom, nom"}
if {$args == "goes to vet"} {return "$dogname Tries to run"}
}
puts [dog fido tail wag]
puts [dog lucky tail droop]
puts [dog charlie eats food]
puts [dog mitts goes to vet]
That will handle multiple dogs pretty well.
One "method," one "if." It's pretty much it, isn't it?
While the dog example is a bit silly and trivial, there are real world use >cases where this inheritence (delegation) game pays off handsomly.
It is partitularly usefull for megawidgets. Tk's base widgets are already >written in an OO style. Also snit "compiles" the code to be far more >efficient than either a stack of if's (or a cascade of if ... elseif ...
else ...). In any case, something like:
snit::type foo {
method a {} {...}
method b {} {...}
method c {} {...}
method d {} {...}
method e {} {...}
method f {} {...}
method g {} {...}
method h {} {...}
method i {} {...}
method j {} {...}
}
is probably easier to read and debug than
proc foo {fun args} {
if {$fun eq "a"} {
...
} elseif {$fun eq "b"} {
...
} elseif {$fun eq "c"} {
...
} elseif {$fun eq "d"} {
...
} elseif {$fun eq "e"} {
...
} elseif {$fun eq "f"} {
...
} elseif {$fun eq "g"} {
...
} elseif {$fun eq "h"} {
...
} elseif {$fun eq "i"} {
...
} elseif {$fun eq "j"} {
...
} else {
error ...
}
}
The other thing is that the dog example has no instance variables (or >options) -- this seems to make the use of OO pointless. Once you add in >instance variables and options, and start having multiple instances of the >"type" (or more significantly, widgets), you will want to have the common >"code" in one shared place and you will want to have some way of keeping >track of instance variables / instance options and that is of course the >whole point of having reusable widgets in the first place...
Single instance classes are pretty pointless -- so are classes where all >instances are totally identical (where all of the class instances are 100% >interchangable and carry no instance specific state). That would like
having a "type" 42 (the specific integer with the specific value of 42).
There are sometimes "classes" like that: there are some specific use cases
-- see "ENSEMBLE COMMANDS" in man snitfaq. I also use this to create "main" >programs where I can encapsulate "global" program data / constants / >parameters (as type variables). I don't really *need* to do this, but it >keeps things tidy and I don't need to use either '::' (global decls) all
over the place and don't have to worry about stepping on some other code's >toes.
As a thought experiment, consider how you'd make your 'dog' proc
'count' (say you have circus dogs that can 'count' ...) and imagine how
to give each of four different dogs their own, independent, counter to
store their current value into.
Then, compare the code you'd need to add to the dog proc to make a
'counting dog' proc with separate counter variables for each dog to the
code necessary to achieve the same result from the counting class
above.
Luc <[email protected]d> wrote:
And how is that better than this:
proc dog {dogname args} {
if {$args == "tail wag"} {return "$dogname Wag, wag"}
if {$args == "tail droop"} {return "$dogname Droop, droop"}
if {$args == "eats food"} {return "$dogname Nom, nom"}
if {$args == "goes to vet"} {return "$dogname Tries to run"}
}
puts [dog fido tail wag]
puts [dog lucky tail droop]
puts [dog charlie eats food]
puts [dog mitts goes to vet]
That will handle multiple dogs pretty well.
One "method," one "if." It's pretty much it, isn't it?
The Tcl 'my' manpage provides a very simple example that should help illuminate why the 'object' method would be better than your 'dog'
example above.
The 'class' example it gives is (modified slightly below):
% oo::class create c {
variable counter
method count {} {
puts [incr counter]
}
}
::c
This creates a class command 'c' that will create objects. So lets
create two objects, o1 and o2:
% c create o1
::o1
% c create o2
::o2
Now, what we have is two objects, that each have their own "counter" variable. The 'counter' variable is not shared between them. Watch
this below:
Make o1 count a few times:
% o1 count
1
% o1 count
2
% o1 count
3
% o1 count
4
% o1 count
5
Now, make o2 count for the first time:
% o2 count
1
o2 returns 1, not 6, so its 'counter' variable is separate from the
'counter' variable in o1 (which is presently storing the value '5').
Now make them both count once each again:
% o1 count
6
% o2 count
2
o1 knows it was at 5, and so it returns 6, and o2 knows it was at 1, so
it returns 2.
And, note, this example got this "separation" of data automatically,
from just this little bit of 'code':
% oo::class create c {
variable counter
method count {} {
puts [incr counter]
}
}
As a thought experiment, consider how you'd make your 'dog' proc
'count' (say you have circus dogs that can 'count' ...) and imagine how
to give each of four different dogs their own, independent, counter to
store their current value into.
Then, compare the code you'd need to add to the dog proc to make a
'counting dog' proc with separate counter variables for each dog to the
code necessary to achieve the same result from the counting class
above.
On Wed, 26 Jun 2024 01:00:40 +0000, Robert Heller wrote:
While the dog example is a bit silly and trivial, there are real world use >cases where this inheritence (delegation) game pays off handsomly.
It is partitularly usefull for megawidgets. Tk's base widgets are already >written in an OO style. Also snit "compiles" the code to be far more >efficient than either a stack of if's (or a cascade of if ... elseif ... >else ...). In any case, something like:
snit::type foo {
method a {} {...}
method b {} {...}
method c {} {...}
method d {} {...}
method e {} {...}
method f {} {...}
method g {} {...}
method h {} {...}
method i {} {...}
method j {} {...}
}
is probably easier to read and debug than
proc foo {fun args} {
if {$fun eq "a"} {
...
} elseif {$fun eq "b"} {
...
} elseif {$fun eq "c"} {
...
} elseif {$fun eq "d"} {
...
} elseif {$fun eq "e"} {
...
} elseif {$fun eq "f"} {
...
} elseif {$fun eq "g"} {
...
} elseif {$fun eq "h"} {
...
} elseif {$fun eq "i"} {
...
} elseif {$fun eq "j"} {
...
} else {
error ...
}
}
The other thing is that the dog example has no instance variables (or >options) -- this seems to make the use of OO pointless. Once you add in >instance variables and options, and start having multiple instances of the >"type" (or more significantly, widgets), you will want to have the common >"code" in one shared place and you will want to have some way of keeping >track of instance variables / instance options and that is of course the >whole point of having reusable widgets in the first place...
Single instance classes are pretty pointless -- so are classes where all >instances are totally identical (where all of the class instances are 100% >interchangable and carry no instance specific state). That would like >having a "type" 42 (the specific integer with the specific value of 42).
There are sometimes "classes" like that: there are some specific use cases >-- see "ENSEMBLE COMMANDS" in man snitfaq. I also use this to create "main" >programs where I can encapsulate "global" program data / constants / >parameters (as type variables). I don't really *need* to do this, but it >keeps things tidy and I don't need to use either '::' (global decls) all >over the place and don't have to worry about stepping on some other code's >toes.
This is a really good explanation that got me interested. I've been
reading up on snit and I like what I see.
But I am stuck in this:
snit::widget TopWindow {
hulltype toplevel
delegate option * to hull
component of
constructor {args} {
wm withdraw $win
wm resizable $win 1 1
wm attributes $win -zoomed 1
wm geometry $win 60x10+0+0
wm title $win "test"
tk appname "test"
bind $win <Alt-q> {exit 0}
bind $win <Alt-Q> {exit 0}
set of [frame $self.of]
pack $of -fill both -expand 1
$self configurelist $args
puts "self is $self"
}
}
TopWindow .toplevel
puts [winfo children .]
output: .
The snit manual says:
"The body of a type constructor is executed once when the type is defined, and never again."
So the part that defines frame $self.of should have been executed, right?
In which case, $self.of is a child of the toplevel widget, right?
Then how come puts [winfo children .] only outputs "." with no mention
of the frame?
Oh, the 'wm withdraw $win' line doesn't seem to be executed either. The
gray box still pops up.
On Wed, 26 Jun 2024 22:32:17 -0000 (UTC), Rich wrote:
As a thought experiment, consider how you'd make your 'dog' proc
'count' (say you have circus dogs that can 'count' ...) and imagine
how to give each of four different dogs their own, independent,
counter to store their current value into.
Then, compare the code you'd need to add to the dog proc to make a >>'counting dog' proc with separate counter variables for each dog to
the code necessary to achieve the same result from the counting class >>above.
lassign "1 1" c_01 c_02
proc countcees {cee} {
upvar \#0 $cee _cee
incr _cee
puts $_cee
}
countcees c_01
2
countcees c_01
3
countcees c_02
2
countcees c_02
3
countcees c_01
4
In reality though, nobody needs a proc for that.
The above is wrong in several ways. Widgets *never* use $self to reference >the widget's path. ($self would only be used to reference methods.)
Secondly, components are "installed" [exception: *typecomponents* are
set]. So the above should be:
On Thu, 27 Jun 2024 02:42:35 +0000, Robert Heller wrote:
The above is wrong in several ways. Widgets *never* use $self to reference >the widget's path. ($self would only be used to reference methods.) >Secondly, components are "installed" [exception: *typecomponents* are
set]. So the above should be:
Thanks!
But I already regret snitting the toplevel.
I mean, is there any benefit in snitting everything or should I rather
just snit the things I know there will be many of?
And apart from very personal preferences, is there any really good
reason for someone to prefer snit over TclOO?
From where I stand (about 2 inches tall so not a great view), it *seems*
that snit is easier. But it also seems that TclOO has better documentation.
I'm also wondering, is this "delegate" business really good for me?
Wouldn't it be better for me to get used to a more traditional (inheritance) approach to OOP? Would TclOO give me the more tradiional approach?
Does it also handle widgets? Can I make megawigets with it?
Snit always seemed to have good enough documentation for me.
Did you read the man pages for *Tk* itself?
snit::widget and snit::widgetadaptor create widget constructors that
behave *EXACTLY* like native Tk widgets.
On Thu, 27 Jun 2024 23:41:16 +0000, Robert Heller wrote:
Snit always seemed to have good enough documentation for me.
Of course I am not very bright (or I have no previous experience with
OOP if you want to be kind), but I am struggling with using snit at all because of this:
The world famous snit faq says:
"For example, the following code creates a read-only text widget... "
etc. etc.
::snit::widgetadaptor rotext {
constructor {args} {
# Create the text widget; turn off its insert cursor
installhull using text -insertwidth 0
# Apply any options passed at creation time.
$self configurelist $args
}
# Disable the text widget's insert and delete methods, to
# make this readonly.
method insert {args} {}
method delete {args} {}
# Enable ins and del as synonyms, so the program can insert and
# delete.
delegate method ins to hull as insert
delegate method del to hull as delete
# Pass all other methods and options to the real text widget, so
# that the remaining behavior is as expected.
delegate method * to hull
delegate option * to hull
}
Yeah, the document has some examples.
Now, how do I call that? I did it like this:
rotext .rox
and nothing happened. Well, I got the Tk gray box.
The world famous snit faq says:
"For example, the following code creates a read-only text widget... "
Where is the text widget then?
After a lot of struggle, I fixed it by adding 'pack $win' within the constructor. Why can't the manual tell me that? Why can't the example
code include it?
There was more struggle.
snit::widget TopWindow {
hulltype toplevel
delegate option * to hull
component of
variable of
constructor {args} {
wm withdraw .
wm resizable $win 1 1
wm attributes $win -zoomed 0
wm geometry $win 600x100+0+0
wm title $win "test"
tk appname "test"
bind $win <Alt-q> {exit 0}
bind $win <Alt-Q> {exit 0}
install of using frame $win.of
pack $of -fill both -expand 1
$self configurelist $args
}
}
TopWindow .toplevel
Now, how do I add the read-only text widget to that toplevel?
Again, a lot of struggle to figure this out:
rotext .toplevel.of.rox
Why can't the manual tell me that?
And I can only do that by explicitly referencing ".toplevel"
which seems inadequate. There should be a variable that contains
that toplevel path. There probably is, but heck if I know what
that is.
I'm still struggling with other things. Anyway, my point is
the documentation has multiple examples of how to create a
type but no examples whatsoever of how to call what the code
is creating. It just assumes the reader knows because it's obvious.
But it took me a long time to guess on my own.
Thank you for your guidance once again.
On Fri, 28 Jun 2024 02:24:19 +0000, Robert Heller wrote:
Did you read the man pages for *Tk* itself?
snit::widget and snit::widgetadaptor create widget constructors that
behave *EXACTLY* like native Tk widgets.
I think I understand, but it took me a long time to grasp because I
think I was lured into something else.
snit::type dog {
# ....
}
::dog
% dog create spot
::spot
spot execute method
So I thought,
snit::widget textbox {
# ....
text -options blahblahblah
}
textbox create textbox1
textbox1 -otheroptions blahblahblah
But it's nothing like that! I thought I was going to create and refer
to widgets by some fancy name of my own choosing, but I'm not.
I was disappointed.
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (2 / 14) |
| Uptime: | 30:56:37 |
| Calls: | 12,108 |
| Calls today: | 8 |
| Files: | 15,006 |
| Messages: | 6,518,265 |