Thread from comp.lang.tcl (7 replies)

Lost in Ashok's teachings
Posted by Luc <luc@sep.invalid> 6 days 13 hours ago

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=20
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=20
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.=20
Thus in our example, deposit and withdraw are exported methods while=20
UpdateBalance is not. Method visibility can be changed by using the=20
export and unexport commands inside a oo::define class definition script.=20
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.=20
Instead, he goes on and on about a lot of other information that I=20
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=20
=E2=80=9Coutside=E2=80=9D the object.

% $acct balance
=E2=86=92 1000000
% $acct deposit 1000
=E2=86=92 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?

--=20
Luc
>>

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Ralf Fassel <ralfixx@gmx.de> 6 days 12 hours ago

* 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'

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Luc <luc@sep.invalid> 6 days 8 hours ago

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
>>

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Ashok <apnmbx-public@yahoo.com> 6 days 4 hours ago

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

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Ralf Fassel <ralfixx@gmx.de> 6 days 4 hours ago

* 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'

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Rich <rich@example.invalid> 5 days 22 hours ago

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.

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Luc <luc@sep.invalid> 5 days 19 hours ago

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
>>

Click on article to view all threads in comp.lang.tcl
Re: Lost in Ashok's teachings
Posted by Rich <rich@example.invalid> 5 days 18 hours ago

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.

Click on article to view all threads in comp.lang.tcl