• My hang-up about OOP (snit)

    From Luc@21:1/5 to All on Tue Jun 25 18:09:28 2024
    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]

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Tue Jun 25 22:29:46 2024
    Luc <[email protected]d> wrote:
    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.


    To do the same with the "proc" you have to rewrite the code to
    be something like:

    proc dog-spot ...
    proc dog-frank ...
    proc dog-sam ...

    Or else you have to modify the dog proc to use a global variable to
    store "different dogs" and take a parameter of "dog-name" so it knows
    which dog to manipulate.

    The "objects" give you that "data separation" as part of the OO
    framework, without you having to build it in manually.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Rich on Tue Jun 25 19:57:30 2024
    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?

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Wed Jun 26 01:00:40 2024
    At Tue, 25 Jun 2024 18:09:28 -0300 Luc <[email protected]d> wrote:


    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]


    snit::type greatdane {
    component common -inherit true
    constructor {args} {
    install common using dog %%AUTO%%
    }
    method bark {} {return "WOOF!"}
    }

    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.


    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Wed Jun 26 01:30:41 2024
    At Tue, 25 Jun 2024 19:57:30 -0300 Luc <[email protected]d> wrote:


    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?

    Nope. How would you handle instance variables? Options? Dog breeds (which would be implemented with derived classes with specialized methods, instance variables and options)? And with a large number of methods, etc., that if
    very quickly becomes unweildly.

    The dog example is probably a bad example to base ALL of OO on. It was only meant as a starting point, like:

    proc HelloWorld {} {
    puts "Hello World"
    }
    HelloWorld

    can be trivially replaced with:

    puts "Hello World"

    and from that *ALONE* one could argue that "proc" is pointless, totally ignoring all of the other reasons why one would use proc.



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Wed Jun 26 04:02:56 2024
    Luc <[email protected]d> wrote:
    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?

    Robert explained that your one 'if' is going to get pretty unweildy
    very fast, esp. if you have different dogs with different behaviors.
    I.e., fido can fetch, lucky refuses to fetch but will chew your
    slippers, charle will roll over and fetch, but never chews your
    slippers.

    But, you are focused on the "call method" part of objects, and that's
    not the part that makes objects actually useful. It's helpful, but the
    real value is the fact that each object is an isolated little block of
    data (with a set of procs that know how to access and manipulate that
    data).

    A better example than the "dog" one for objects and the data
    encapsulation aspects is to instead think "bank accounts" (this won't
    be proper snit syntax below, but hopefully you'll follow):

    snit::type savings {
    variable balance 0 ;# assume this initalizes 'balance' to zero when a
    #new object of type 'savings' is created

    method deposit {amount} {
    set balance [expr {$balance + $amount]
    }

    method withdraw {amount} {
    set balance [expr {$balance - $amount]
    }

    method inquire {} {
    return $balance
    }
    }

    Now we create accounts for fred and barney:

    savings fred
    savings barney

    And we have two objects, "fred" and "barney" that both have a
    "balance" variable, but... their balance variables are actually
    separaate (you would not want your bank account to be "shared" with
    everyone else who banks at that bank, right).

    So if fred deposts 20, you can do:

    fred depost 20

    Then, if you 'inquire' about the balances you get:

    fred inquire
    20
    barney inquire
    0

    Different answers, because each object has its own unique set of
    variables (i.e., data) that is private for just it. Whatever you do to barney's account, fred's balance remains the same.

    This is where the real 'magic' of objects starts to show it's value.
    You were building someing some months ago where I pointed out that
    you'd accidentially worked your way into building a very simple object
    by accident. IIRC you were trying to track multiple windows in a text
    or on a canvas for something, and you'd created a set of namespaces or something, one per each window, to store the variables needed for that
    window apart from the others.

    Had that been 'objects' from the start, you'd have created a class that
    defined the variables each window needed, and the methods to act on
    those variables (and update the window contents) and then you could
    have just created "new" objects, one per window, and had all the
    framework of separate variables and procs all taken care of by the OO
    system.

    And since objects can have an 'initializer', you could have had the
    creation of the object also create the window associated with the
    object automatically, and store away the window names in object
    variables to use to manipulate the window. And a finalizer to destroy
    the windows that were created when the object was destroyed. So you
    could have done:

    win new abc

    and gotten the window created, plus all the 'procs' to do things with
    the window, and then when done with it called

    abc destroy

    and had the windows also destroyed and all the variables deleted that
    were part of the object -- all automatically.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Wed Jun 26 04:09:27 2024
    Luc <[email protected]d> wrote:
    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?

    Also, Ashok's chapter from his Tcl book that describes 8.6's OO
    subsystem is available online:

    https://www.magicsplat.com/articles/oo.html

    You might consider carefully reading it. It is not that long, and
    Ashok uses the "bank account" metaphor instead of the "dog" one, which
    provides some intuitiveness into the "separate variables in each
    object" aspect (you don't want your bank account data shared with
    others you did not approve it to be shared with).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Wed Jun 26 22:32:17 2024
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Wed Jun 26 22:09:32 2024
    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.

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Rich on Wed Jun 26 22:02:00 2024
    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.

    incr c_o1
    1
    incr c_o2
    1
    incr c_o2
    2
    incr c_o2
    3
    incr c_o2
    4
    incr c_o1
    2

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to Rich on Thu Jun 27 02:13:16 2024
    At Wed, 26 Jun 2024 22:32:17 -0000 (UTC) Rich <[email protected]d> wrote:


    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.

    And to totally blow Luc's mind, consider this "class":

    snit::widgetadaptor ROText {
    delegate method * to hull
    delegate option * to hull except {-background -borderwidth -font
    -foreground -highlightbackground -highlightcolor -highlightthickness
    -relief -insertbackground -selectbackground -insertborderwidth
    -selectborderwidth -selectforeground -padx -pady}
    typeconstructor {
    ttk::style configure ROText \
    -background ttk::theme::default::colors(-frame) \
    -borderwidth 1 \
    -font TkFixedFont \
    -foreground black \
    -relief sunken \
    -selectbackground ttk::theme::default::colors(-selectbg) \
    -selectborderwidth 1 \
    -selectforeground ttk::theme::default::colors(-selectfg) \
    -padx 0 -pady 0
    }
    option -style -default ROText
    method _themeUpdated {} {
    #puts stderr "*** $self _themeUpdated"
    foreach o {-background -borderwidth -font -foreground
    -highlightbackground -highlightcolor -highlightthickness -relief
    -insertbackground -selectbackground -insertborderwidth
    -selectborderwidth -selectforeground -padx -pady} {
    if {![catch {ttk::style lookup $options(-style) $o} ov]} {
    #if {"$ov" eq ""} {continue}
    #puts stderr "*** $self _themeUpdated: $o $ov"
    catch {$hull configure $o $ov}
    }
    }
    }
    constructor {args} {
    installhull using text
    $self configurelist $args
    set indx [lsearch [bindtags $win] Text]
    bindtags $win [lreplace [bindtags $win] $indx $indx ROText]
    bind $win <<ThemeChanged>> [mymethod _themeUpdated]
    $self _themeUpdated
    }
    }

    (A large chunk of the code is ommited -- the full file is here: https://github.com/RobertPHeller/ModelRRSystem/blob/master/trunk/Scripts/Common/snitrotext.tcl
    -- the whole laundry list of bindings are left out
    )

    What this deceptively simple "widgetadaptor" does is two things: It "wraps" around the Tk "text" widget and makes it readonly (not user editable) -- great for things like log output, etc. At the same time, it makes it themed, just like the various ttk:: widgets. (A snit::widgetadaptor is a special case of a snit::type intended to "wrap" around an arbitary widget.)

    The only methods added are the theme related methods. It takes away the old school styling options (-background -borderwidth -font -foreground, etc.) and replaces them with a -style option. It also changes the bindtags to remove the text insert key and mouse bindings (eg generic key press and paste bindings). Otherwise it is otherwise just like a garden variety text widget. Note that this in fact leverages the fact all of Tk's widgets are in fact functionally OO, dispite being coded in plain C. Actually *lots* of Tcl is functionally OO, dispite not technically being OO. SNIT can "inheirt" (delagate) to Tcl "commands" that are not snit "types" -- all the commands have to have are a functional structure like "<command> sub-command ..." and (optionally) implement the configure and cget "methods" (sub-commands).




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Thu Jun 27 02:42:35 2024
    At Wed, 26 Jun 2024 22:09:32 -0300 Luc <[email protected]d> wrote:


    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]

    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:

    install of using frame $win.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.

    The gray box is ".", but your snit::widget TopLevel is creating a *new* toplevel called .toplevel and withdrawing it and never deiconifying it. I explained the problem with the frame "of" above.

    marchhare% tclsh
    % package require snit
    2.3.2
    % 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}

    install of using frame $win.of
    pack $of -fill both -expand 1

    $self configurelist $args
    puts "self is $self"
    }
    }
    ::TopWindow
    % TopWindow .toplevel
    self is .toplevel
    toplevel
    % puts [winfo children .]
    toplevel
    % winfo children .toplevel
    toplevel.of
    %



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Thu Jun 27 02:33:58 2024
    Luc <[email protected]d> wrote:
    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.

    True, but you are missing the forest for the trees.

    Imagine extending your example above to 273 different "counters" (or
    managing 273 different sets of complicated [more than just a single
    counter] pieces of independent data) or imagine managing 15 different
    windows with differing (not shared) data, in the same app.

    That's the point, the "do it with a proc and a bunch of globals"
    method, where you have to modify the code to extend from 14 to 15 windows
    (and likely have to add a 15'th 'then' clause to 95 different 'if'
    statements) vs. having the system handle the "separation" details and your code
    change to go from 14 to 15 is to to just create 15 objects instead of 14.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Thu Jun 27 18:08:32 2024
    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?

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Thu Jun 27 23:41:16 2024
    At Thu, 27 Jun 2024 18:08:32 -0300 Luc <[email protected]d> wrote:


    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?

    Any thing where you have encapsulated data or state benefits from being a class, if only to document the encapsulated data or state, even if there is only ever one or if it is in fact an instance-less snit::type. It is a convenient way to gather the "global" data that is part of a logical function block or programming unit. One major problem to many scripting languages (including especially Tcl), is polluting the global namespace. Yes you can simply create a namespace filled with variables and procs rather than a snit::type, and get to the same logical situation. It is that snit takes care of all of the "infrastructure" to implement that and "hides" some of that infrastructure and provides some special support features (like the typeconstructor) that are convenient.

    The other benefit is creating a self-contained code library. You may only have one toplevel in any given program, but you are likely to write many programs. You might have a certain "style" of toplevel you like and want to reuse in
    your many programs. Creating this styled toplevel and parameterizing it in a "library" package as a snit::widget or snit::widgetadaptor is useful, because you get reuse this code.


    And apart from very personal preferences, is there any really good
    reason for someone to prefer snit over TclOO?

    In my case I started using snit before TclOO became available, so...


    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.

    Snit always seemed to have good enough documentation for me. OTOH, I was already very familiar with OO (C++, Java, etc.), so I never needed a general
    OO primer. Maybe Snit might not be good for a first time OO programmer -- I can't really say.


    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?


    I don't know about whether TclOO handles widgets or not. The delegate business seems really good for everything I do in Tcl -- it fits well with Tk's widget methodology. Tradiional inheritance (ala C++ and Java) can be a bitch sometimes (you don't want to hear my rants about Java GUI programming). I would say that one is not "better" than the other. Just different and they solve different problems differently -- I do a lot of C++ programming (generally NOT GUI work) and Tcl/Tk programming (using SNIT). So I use both flavors of "inheritance"
    all the time, just doing different things. I know, that is not really the sort of answer you are looking for, but there it is. Sometimes you need a hammer and sometimes you need a screwdriver, so you have both in your toolbox. One
    is not really "better" than the other, just that hammers work better with
    nails and screwdrivers work better with screws...

    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Thu Jun 27 21:27:04 2024
    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.

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Robert Heller on Fri Jun 28 00:06:29 2024
    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.


    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Fri Jun 28 02:24:19 2024
    At Thu, 27 Jun 2024 21:27:04 -0300 Luc <[email protected]d> wrote:


    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 snit::widget and snit::widgetadaptor entities work *exactly* like "native" Tk widgets.

    For a regular text widget you would do:

    test .t
    pack .t -fill both -expand yes

    so for a rotext, you would do:

    rotext .rox
    pack .rox -fill both -expand yes

    The widget constructor doesn't 'automatically' pack the widget. Although pack is probably the most common geometry manager, you could use grid or place, so the choice of geometry manager (and its options) is left up to the programmer.



    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?

    *DON'T* put the pack in the constructor! One of the common ways to create a Scroll Window (a frame with scroll bars intended to contain a scrollable
    widget (like a text or canvas or listbox, etc.) uses grid and if the text is already packed, this will fail with an error.

    ALL Tk widget constructors leave the geometry manager (pack, grid, or place) out. It is the responsibility of the programmer to add the appropriate
    geometry manager code after creating the widget. This is always how this is done. The geometry managers have many options controlling how the widget's geometry is managed and none of these make sense as options to the widget creation. There are also cases where the widget might not get set up with a geometry manager right away or might get disassociated with a geometry manager at some point (eg pack forget). Geometry management is a whole separate process, and is always handled separately from widget creation.



    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?

    Did you read the man pages for *Tk* itself?

    snit::widget and snit::widgetadaptor create widget constructors that behave *EXACTLY* like native Tk widgets. All of the things talked about in the man pages for Tk and all of the native Tk widgets and Tk geometry managers apply
    to widgets created with snit::widget and snit::widgetadaptor apply. snit::widget and snit::widgetadaptor create "commands" that can be dropped
    into anyplace a any native Tk widget creation command would be at home.

    snit::widgetadaptor MySpecialButton {
    ..
    }

    then

    set B01 [MySpecialButton .b ...]
    pack $B01

    *JUST LIKE*

    set B01 [button .b ...]
    pack $B01

    *OR*

    set B01 [ttk::button .b ...]
    pack $B01


    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.

    set mytoplevel [TopLevel .toplevel]

    *EXACTLY* like:

    set mytoplevel [toplevel .toplevel]


    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.

    That is because it is already documented elsewhere in the Tk man pages.



    Thank you for your guidance once again.


    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to [email protected] on Fri Jun 28 11:49:11 2024
    At Fri, 28 Jun 2024 00:06:29 -0300 Luc <[email protected]d> wrote:


    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.

    Tk widgets, whether the "built in" (native) ones or ones created by snit (or BWidget or any other Tcl/Tk widget extension) all *have* to obey the
    convention that Tk widgets are in a hierarchical path: ".parent.child.grandchild" -- it does not matter if parent was created by a Tk widget and child was created by a snit::widget and grandchild was created by a BWidget (another Tk widget extension framework) class. This is *required* by how the underlying structure of the Tk GUI subsystem works. All "widgets" (windows) have to the be the child of some other widget (window) and container widgets (eg toplevels, frames, etc.) can have children widgets. Only "." has
    no parent and is in fact the final ancestor of all widgets. I believe this parent and child structure is a standard feature of all GUI subsystems, not just Tk. There are reasons for that, which could probably fill a whole
    textbook / university course to explain.

    Snit's "types" are not constrained by Tk -- they are their own thing independent of Tk. Snit's "widgets" and "widgetadaptors" do use the same internal structure as snit "types" (variables, methods, options, and components), but their naming conventions are tied (hardwired) to follow the same conventions as any other widget framework.




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)