• Lost in Ashok's teachings

    From Luc@luc@sep.invalid to comp.lang.tcl on Thu Sep 26 05:04:22 2024
    From Newsgroup: comp.lang.tcl

    I am trying to learn from this page: https://www.magicsplat.com/articles/oo.html
    It is written by the legendary Ashok. The author that so many people
    here so often rush to recommend.
    I am not getting along well with it.
    His first lesson: create a class:
    % oo::class create Account
    Next lesson: defining data members:
    % oo::define Account {
    variable AccountNumber Balance
    }
    Then, defining methods:
    oo::define Account {
    method UpdateBalance {change} {
    set Balance [+ $Balance $change]
    return $Balance
    }
    method balance {} { return $Balance }
    method withdraw {amount} {
    return [my UpdateBalance -$amount]
    }
    method deposit {amount} {
    return [my UpdateBalance $amount]
    }
    export UpdateBalance
    }
    Sadly, Ashok embraces the most traditional way of teaching programming
    or anything related to IT: assuming the reader is some kind of ChatGPT
    sponge that can instantly absorb all and any information that is fed
    along the way, so he distracts me with a lot of additional information
    that I REALLY think should only be fed later, and which I am skipping here.
    But I had to take note of this:
    "Methods that begin with a lower case letter are exported by default.
    Thus in our example, deposit and withdraw are exported methods while UpdateBalance is not. Method visibility can be changed by using the
    export and unexport commands inside a oo::define class definition script.
    Thus
    oo::define Account {export UpdateBalance}
    OK. I did that. So here is my script:
    ----------- oop.tcl -----------------
    oo::class create Account
    oo::define Account {
    variable AccountNumber Balance
    }
    oo::define Account {
    method UpdateBalance {change} {
    set Balance [+ $Balance $change]
    return $Balance
    }
    method balance {} { return $Balance }
    method withdraw {amount} {
    return [my UpdateBalance -$amount]
    }
    method deposit {amount} {
    return [my UpdateBalance $amount]
    }
    export UpdateBalance
    }
    ----------------------------------
    And he provides zero information on how to USE that thing. Zero.
    Instead, he goes on and on about a lot of other information that I
    REALLY think should be reserved for later.
    Finally, I run into "3. Working with objects"
    % set acct [Account new 3-14159265]
    % Account create smith_account 2-71828182
    OK. So I add those two lines to my script. Upon running it, no complaints
    by the compiler. But there is no output either. Let's add some action: ----------- oop.tcl -----------------
    oo::class create Account
    oo::define Account {
    variable AccountNumber Balance
    }
    oo::define Account {
    method UpdateBalance {change} {
    set Balance [+ $Balance $change]
    return $Balance
    }
    method balance {} { return $Balance }
    method withdraw {amount} {
    return [my UpdateBalance -$amount]
    }
    method deposit {amount} {
    return [my UpdateBalance $amount]
    }
    }
    oo::define Account {export UpdateBalance}
    set acct [Account new 3-14159265]
    Account create smith_account 2-71828182
    $acct deposit 132
    ----------------------------------
    There, I broke the toy.
    can't read "Balance": no such variable
    while executing
    "+ $Balance $change"
    (class "::Account" method "UpdateBalance" line 2)
    invoked from within
    "my UpdateBalance $amount"
    (class "::Account" method "deposit" line 2)
    invoked from within
    "$acct deposit 132"
    (file "oop.tcl" line 41)
    Compilation failed.
    I tried a lot of ideas and all of them run into the same problem:
    the compiler has no knowledge of this Balance variable I speak of.
    Reading on, I find this:
    3.3. Invoking methods
    This is the form used to invoke a method on the object from code
    “outside” the object.
    % $acct balance
    → 1000000
    % $acct deposit 1000
    → 1001000
    Oh yeah? OK. Let's add those lines to the script then:
    $acct balance
    can't read "Balance": no such variable
    while executing
    "return $Balance "
    (class "::Account" method "balance" line 1)
    invoked from within
    "$acct balance"
    (file "oop.tcl" line 41)
    Compilation failed.
    $acct deposit 1000
    can't read "Balance": no such variable
    while executing
    "+ $Balance $change"
    (class "::Account" method "UpdateBalance" line 2)
    invoked from within
    "my UpdateBalance $amount"
    (class "::Account" method "deposit" line 2)
    invoked from within
    "$acct deposit 1000"
    (file "oop.tcl" line 41)
    Compilation failed.
    I wonder if the code provided in the explanation is really correct
    and tested. Why is the 'Balance' variable adamantly not recognized?
    I am also intrigued by this line:
    set Balance [+ $Balance $change]
    Where did he ever define '+' as a command? There is zero explanation
    of it.
    I am very confused. Any comments, please?
    --
    Luc

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Thu Sep 26 11:21:14 2024
    From Newsgroup: comp.lang.tcl

    * Luc <luc@sep.invalid>
    | ----------- oop.tcl -----------------
    | oo::class create Account
    | oo::define Account {
    | variable AccountNumber Balance
    | }

    | oo::define Account {
    | method UpdateBalance {change} {
    | set Balance [+ $Balance $change]
    | return $Balance
    | }
    | method balance {} { return $Balance }
    | method withdraw {amount} {
    | return [my UpdateBalance -$amount]
    | }
    | method deposit {amount} {
    | return [my UpdateBalance $amount]
    | }
    | }

    | oo::define Account {export UpdateBalance}
    | set acct [Account new 3-14159265]
    | Account create smith_account 2-71828182
    | $acct deposit 132
    | ----------------------------------

    | There, I broke the toy.

    | can't read "Balance": no such variable
    | while executing
    | "+ $Balance $change"
    | (class "::Account" method "UpdateBalance" line 2)
    | invoked from within
    | "my UpdateBalance $amount"
    | (class "::Account" method "deposit" line 2)
    | invoked from within
    | "$acct deposit 132"
    | (file "oop.tcl" line 41)
    | Compilation failed.

    | I tried a lot of ideas and all of them run into the same problem:
    | the compiler has no knowledge of this Balance variable I speak of.

    You have ommitted the constructor from Ashoks example which initializes
    the 'Balance' variable. In your class, this variable is simply not initialized, which leads to the error.

    This is the same with every variable in TCL:

    global foo
    set foo
    => can't read "foo": no such variable

    namespace eval foo {
    variable bar
    set bar
    }
    => can't read "bar": no such variable

    You need to initialize (=set) a variable before you can use it.
    How the initialization is done depends on the context.

    namespace eval foo {
    variable bar init-value
    set bar
    }
    => init-value

    Thus if you have variables in a class, you most probably need a
    constructor to initialize them.

    This is also true for many OO languages...

    HTH
    R'
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Thu Sep 26 10:15:00 2024
    From Newsgroup: comp.lang.tcl

    On Thu, 26 Sep 2024 11:21:14 +0200, Ralf Fassel wrote:

    You have ommitted the constructor from Ashoks example which initializes
    the 'Balance' variable. In your class, this variable is simply not >initialized, which leads to the error.

    True. Thanks.

    What about that plus sign? Where is it defined?
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ashok@apnmbx-public@yahoo.com to comp.lang.tcl on Thu Sep 26 22:17:46 2024
    From Newsgroup: comp.lang.tcl

    On 9/26/2024 6:45 PM, Luc wrote:
    On Thu, 26 Sep 2024 11:21:14 +0200, Ralf Fassel wrote:

    You have ommitted the constructor from Ashoks example which initializes
    the 'Balance' variable. In your class, this variable is simply not
    initialized, which leads to the error.

    True. Thanks.

    What about that plus sign? Where is it defined?



    Replace it with tcl::mathop::+

    Somewhere earlier in the book, I think I mention that the rest of the
    book assumes a "namespace path tcl::mathop" which is why there is that standalone "+". Was a bad idea in hindsight just to save the clutter of tcl::mathop:: prefixes everywhere.

    Regarding the code testing, the book build process runs every snippet of
    code and the output generated in the book is actually from running each example at book build time. Very few exceptions to that. Any errors
    abort the build process. The OO code should therefore run without errors
    (in the book, not sure of the online version which was an earlier draft).

    As for the rest of your comments, fair enough that you do not like the approach. You might try the other books in this space, like the one from
    Clif Flynt, or the online tutorial at https://wiki.tcl-lang.org/page/Tcl+Tutorial+Lesson+OOP1

    /Ashok
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Thu Sep 26 18:53:11 2024
    From Newsgroup: comp.lang.tcl

    * Luc <luc@sep.invalid>
    | On Thu, 26 Sep 2024 11:21:14 +0200, Ralf Fassel wrote:
    | What about that plus sign? Where is it defined?

    That is TCLs mathop:

    https://www.tcl-lang.org/man/tcl/TclCmd/mathop.htm

    % namespace path {::tcl::mathop}
    % + 4 5
    9

    See also
    https://wiki.tcl-lang.org/page/tcl%3A%3Amathop?R=0&O=mathop&W=

    HTH
    R'
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Thu Sep 26 22:28:17 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    On Thu, 26 Sep 2024 11:21:14 +0200, Ralf Fassel wrote:
    What about that plus sign? Where is it defined?

    A bit of 'context' would help us in actually knowing to what you are referring. I'm assuming you mean this construct from your original
    post:

    [+ $var $var]

    That + is the "proc" version of the addition operator. It is
    accessible as either:

    $ rlwrap tclsh
    % ::tcl::mathop::+ 1 2
    3
    %

    Or by setting up a namespace path within your current namespace, which
    then allows you to call the proc without the absolute namespace names:

    % namespace path {::tcl::mathop}
    % + 3 4
    7

    You can also access the math functions (i.e., sin(), abs() etc) in a similar way (they exist in ::tcl::mathfunc::)

    % namespace path {::tcl::mathop ::tcl::mathfunc}
    % sin .2
    0.19866933079506122
    % abs -4
    4

    Presumably, somewhere in an earlier chapter of Ashok's book, he
    detailed the above little setting to be able to call the math operators (and/or functions) as procs. But as the full book is not on the web,
    you would not have had the benefit of having read that material prior
    to reading the OO chapter that is available.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Thu Sep 26 22:59:56 2024
    From Newsgroup: comp.lang.tcl

    On Thu, 26 Sep 2024 22:17:46 +0530, Ashok wrote:

    Replace it with tcl::mathop::+
    Somewhere earlier in the book, I think I mention that the rest of the
    book assumes a "namespace path tcl::mathop" which is why there is that >standalone "+". Was a bad idea in hindsight just to save the clutter of >tcl::mathop:: prefixes everywhere.

    **************************
    On Thu, 26 Sep 2024 18:53:11 +0200, Ralf Fassel wrote:

    That is TCLs mathop:
    https://www.tcl-lang.org/man/tcl/TclCmd/mathop.htm

    % namespace path {::tcl::mathop}
    % + 4 5
    9

    See also
    https://wiki.tcl-lang.org/page/tcl%3A%3Amathop?R=0&O=mathop&W=

    **************************
    On Thu, 26 Sep 2024 22:28:17 -0000 (UTC), Rich wrote:

    You can also access the math functions (i.e., sin(), abs() etc) in a
    similar way (they exist in ::tcl::mathfunc::)

    % namespace path {::tcl::mathop ::tcl::mathfunc}
    % sin .2
    0.19866933079506122
    % abs -4
    4

    **************************

    I was aware of math operations, but have used them very rarely if ever,
    so I didn't make the connection. Most important, I had no idea we could overload our entire code with all the math operations by importing a namespace. I'm not sure I like it, but it's good to know.

    As usual, I am glad I asked despite my usual hesitation. I always end up learning when I ask. Many thanks. I really appreciate it.
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Fri Sep 27 02:48:38 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:

    I was aware of math operations, but have used them very rarely if ever,
    so I didn't make the connection. Most important, I had no idea we could overload our entire code with all the math operations by importing a namespace. I'm not sure I like it, but it's good to know.

    Small nitpick, "namespace path" is not "importing a namespace".
    Importing is an entirely different operation, with its own command to
    cause an 'import' (i.e., 'namespace import').

    The "namespace path" works in a way analagous to the "PATH="
    environment variable for Unix shells. It gives the Tcl interpreter a
    list of "places" to look for commands when your code within the
    namespace attempts to call the command. I.e., what it does is when the command you are calling in your current namespace is not defined in
    that same namespace, then the interpreter looks in each namespace in
    the "path", and if it finds the command defined in one of those
    namespaces, that is the command that gets called.

    So, if you have this:

    namespace eval ::example {
    namespace path {::tcl::mathop}
    variable a [+ 3 5]
    }

    Then what happens when the interpreter is executing the variable
    command is it see's a call to "+". So first it looks for:

    ::example::+

    But that is not defined (assume the above is the totality of the
    definition of ::example).

    So next it looks to each namespace in the "namespace path". I.e. it
    now looks for:

    ::tcl::mathop::+

    And since that one *is* defined (by Tcl itself) it then executes ::tcl::mathop::+ with parameters 3 and 5.

    --- Synchronet 3.20a-Linux NewsLink 1.114