Check-in [ca2e14f744]

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add more examples to 500
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ca2e14f744ca56b23991be42b2e509c5c82092603d4712696b80d1f3085d2c49
User & Date: dkf 2018-05-25 09:44:19
Context
2018-05-25
21:51
TIP #509: Initial draft complete check-in: 61ea8fa199 user: fbonnet tags: trunk
09:44
Add more examples to 500 check-in: ca2e14f744 user: dkf tags: trunk
2018-05-24
16:22
Initiated TIP #509: Implement reentrant mutexes on all platforms check-in: f14eed0570 user: fbonnet tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to tip/500.md.

117
118
119
120
121
122
123

124
125

126
127
128

129
130
131
132
133
134
135

136
137

138
139
140
141

142
143
144

145
146
147
148
149

150
151
152
153
154
155
156
...
172
173
174
175
176
177
178

179
180
181
182
183
184
185
...
200
201
202
203
204
205
206
207
208



209
210
211
212
213
214
215
...
236
237
238
239
240
241
242
243






















244
245
246
247
248
249
250

251
252
253
254
255
256
257
...
272
273
274
275
276
277
278
279
280




















































































281
282
283
284

285
286
287

288
289
290
291

292



293
294
295
296
297
298

299
300

301
302
303
304

305
306
307
308
309
310
311

312

313

314

315

316

317
318
319
320
























































321
322
323
324
325
326
327
328
In particular:

    oo::class create Top {
        method Foo {} {
            puts "This is Top::Foo for [self]"
        }
    }

    oo::class create Super {
        superclass Top

        private method Foo {} {
            puts "This is Super::Foo for [self]"
        }

        method bar {other} {
            puts "This is Super::bar for [self]"
            my Foo
            [self] Foo
            $other Foo
        }
    }

    oo::class create Sub {
        superclass Super

        method Foo {} {
            puts "This is Sub::Foo for [self]"
            next
        }

        private method bar {other} {
            error "This should be unreachable"
        }

        method grill {} {
            puts "This is Sub::grill for [self]"
            my Foo
        }
    }

    Sub create obj1
    Sub create obj2
    obj1 bar obj2
    obj1 grill

is expected to print out:

................................................................................
Private methods are created calling **oo::define** (and **oo::objdefine**, and
via the constructor of **oo::class**) like this:

    oo::define TheClass {
        private method foo {...} {
            ...
        }

        private forward bar  SomeOtherCommand
    }

At the C API level, private methods can be directly created by setting the
*flags* parameter (called *isPublic* in older versions of the API) of
`Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant
`TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private
................................................................................
<dd>Reports the truly private methods only.</dd></dl>

The **-scope** option causes the **-all** and **-private** options to be
ignored.

# Detailed Proposal: Variables

Variables are more complex, as they necessarily can be named by other commands
(e.g., so that they can be **vwait**ed upon). This requires them to be named



by compounding the user-supplied name (to reduce confusion) and a unique
identifier that identifies the particular context of the name. Now, one of the
trickier things about TclOO is that it does not provide fixed names for
objects; the **rename** command is supported. Because of that, the
fully-qualified name of an object or class, while unique, is not a stable
identifier. The full name of the backing namespace *is* stably-named (as
namespaces cannot be renamed), but only has a unique name when the
................................................................................
created by declaration; that's up to an appropriate method to actually do (and
the variable can be created as normal scalar, array or link). When a method
runs in the relevant class, the class's variable resolver will map the private
variable in with the user-expected name.

The **variable** and **varname** methods of **oo::object** will be aware of
the mechanism, respecting what names are visible in the context that invokes
them.























## Creation and Introspection

A private variable mapping is created like this:

    oo::define Foo {
        private variables x y

        method foo {} {
            set x 1
            set y(z) 2
        }
    }

That is, the **private** declaration command described above (for methods)
................................................................................

A supporting introspector is also added, **info object creationid**, which
returns the creation ID of any existing object. It also applies to classes.
Again, note that creation IDs are _always_ system-allocated and are _never_
guaranteed to be unique between interpreters, either in multiple processes or
in the same thread or process; they are only ever locally unique.

# Example





















































































    oo::class create LabelEqual {
        constructor {label} {
            set [my varname label] $label
        }

        private {
            # Two private declarations in the same block
            variable label

            method getLabel {} {
                return $label
            }
        }

        method equals {other} {



            expr {$label eq [$other getLabel]}
        }
    }

    oo::class create Evaluated {
        superclass LabelEqual

        # Poorly chosen variable name! Happens too easily in real life
        variable label

        constructor {expression} {
            next $expression
            set label [expr $expression]
        }

        method value {} {
            return $label
        }
    }

    set expr1 [Evaluated new {1 + 2 + 3}]
    set expr2 [Evaluated new {3 + 2 + 1}]

    puts "one is two? [$expr1 equals $expr2]"

    # Prints: one is two? 0

    puts "one=[$expr1 value] two=[$expr2 value]"

    # Prints: one=6 two=6

    puts [info vars [info object namespace $expr1]::*]

    # Prints something like: {::oo::Obj13::11 : label} ::oo::Obj13::label
    catch {$expr2 getLabel} msg
    puts $msg
    # Prints: unknown method "getLabel": must be destroy, equals or value

























































# Implementation

See the [`tip-500` branch](https://core.tcl.tk/tcl/timeline?r=tip-500).

# Copyright

This document has been placed in the public domain.






>


>



>







>


>




>



>





>







 







>







 







|
|
>
>
>







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|
>







 







|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>



>




>

>
>
>






>


>




>







>

>
|
>

>
|
>

>
|
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
In particular:

    oo::class create Top {
        method Foo {} {
            puts "This is Top::Foo for [self]"
        }
    }

    oo::class create Super {
        superclass Top

        private method Foo {} {
            puts "This is Super::Foo for [self]"
        }

        method bar {other} {
            puts "This is Super::bar for [self]"
            my Foo
            [self] Foo
            $other Foo
        }
    }

    oo::class create Sub {
        superclass Super

        method Foo {} {
            puts "This is Sub::Foo for [self]"
            next
        }

        private method bar {other} {
            error "This should be unreachable"
        }

        method grill {} {
            puts "This is Sub::grill for [self]"
            my Foo
        }
    }

    Sub create obj1
    Sub create obj2
    obj1 bar obj2
    obj1 grill

is expected to print out:

................................................................................
Private methods are created calling **oo::define** (and **oo::objdefine**, and
via the constructor of **oo::class**) like this:

    oo::define TheClass {
        private method foo {...} {
            ...
        }

        private forward bar  SomeOtherCommand
    }

At the C API level, private methods can be directly created by setting the
*flags* parameter (called *isPublic* in older versions of the API) of
`Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant
`TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private
................................................................................
<dd>Reports the truly private methods only.</dd></dl>

The **-scope** option causes the **-all** and **-private** options to be
ignored.

# Detailed Proposal: Variables

Variables are more complex, conceptually, as they necessarily can be named by
other commands (e.g., so that they can be **vwait**ed upon or used with Tk
widgets; the key underlying operation in both of these cases is that they must
support traces) and so cannot be completely separated from the rest of Tcl.
This requires them to be named
by compounding the user-supplied name (to reduce confusion) and a unique
identifier that identifies the particular context of the name. Now, one of the
trickier things about TclOO is that it does not provide fixed names for
objects; the **rename** command is supported. Because of that, the
fully-qualified name of an object or class, while unique, is not a stable
identifier. The full name of the backing namespace *is* stably-named (as
namespaces cannot be renamed), but only has a unique name when the
................................................................................
created by declaration; that's up to an appropriate method to actually do (and
the variable can be created as normal scalar, array or link). When a method
runs in the relevant class, the class's variable resolver will map the private
variable in with the user-expected name.

The **variable** and **varname** methods of **oo::object** will be aware of
the mechanism, respecting what names are visible in the context that invokes
them. Thus, if **varname** is called from a context that would be able to see
the private variable, `foobar`, it will return the fully qualified name of the
Tcl variable variable in the target object's namespace that acts as the
storage of that variable; when the declaring class has creation ID `123` and
the instance of that class has namespace `::oo::Obj456`, the result of
`my varname foobar` will be `::oo::Obj456::123 : foobar`; the parts of that
name are:

 * “`::oo::Obj456`” — Fully-qualified instance namespace name; private
   variables are still ultimately variables that exist on object instances.
 * “`::`” — Namespace separator.
 * “`123`” — Declaring context ID, from the class that holds the definition of
   the private variable.
 * “` : `” — Private variable separator; a space, a colon, and another space.
   This is chosen to be very unlikely to be in user scripts by accident (as
   variable names with spaces and single colons are traditionally known to be
   awkward to use explicitly).
 * “`foobar`” — User-supplied variable name fragment.

Similarly, the **variable** method can be used to bring the private variable
into scope; this would not normally be necessary, but is useful when the usual
resolution rules for the variable are suspended (such as when a variable with
that name is given as a formal argument to a constructor).

## Creation and Introspection

A private variable mapping is created like this:

    oo::define Foo {
        private variable x y

        method foo {} {
            set x 1
            set y(z) 2
        }
    }

That is, the **private** declaration command described above (for methods)
................................................................................

A supporting introspector is also added, **info object creationid**, which
returns the creation ID of any existing object. It also applies to classes.
Again, note that creation IDs are _always_ system-allocated and are _never_
guaranteed to be unique between interpreters, either in multiple processes or
in the same thread or process; they are only ever locally unique.

# Examples

This example shows a private variable linked to a Tk entry:

    oo::class create Editable {
        variable w;            # Allow subclasses to see this
        private variable val;  # Hide this from subclasses

        constructor {widgetName} {
            set val {}
            set w [entry $widgetName -textvariable [my varname val]
        }

        method value {args} {
            if {[llength $args] == 0} {
                return $val
            } elseif {[llength $args] == 1} {
                lassign $args val
                return
            }
            return -code error "wrong # args: ..."
        }

        method trace {callbackLambda} {
            trace add variable val write [list apply $callbackLambda]
        } 
    }

    Editable create field .e
    field trace {args {
        puts "field is now [field value]"
    }}
    field value "Set the contents"

Ths is an example of private methods. It shows how private methods can be used
to manage the complexity of a method without making its API (as exposed to
either subclasses or the rest of Tcl) overly complex.

    oo::class create Modulator {
        # The exported interface to the class
        method modulate {args} {
            try {
                foreach value $args {
                    my modulateOne $value
                }
            } on error {msg opts} {
                tailcall return {*}[my reworkError $msg $opts]
            }
        }

        private {
            # Do something with the single value; practical code would do more
            method modulateOne {value} {
                puts "this would modulate with $value"
                if {$value > 5} {
                    error "this is bad"
                }
            }

            # Simple way to hide the internal structure of the class from errors
            method reworkError {msg opts} {
                dict set opts -errorinfo "problem with modulating: $msg"
                list -options $opts $msg
            }
        }
    }

    set m [Modulator new]

    catch {$m modulate 4 5 6 7 8} -> opts
    puts [dict get $opts -errorinfo]
    # Prints something like:
    #    this would modulate with 4
    #    this would modulate with 5
    #    this would modulate with 6
    #    problem with modulating: this is bad
    #        invoked from within
    #    "::oo::Obj15 modulate 5 6 7 8"

    catch {$m no.such.method} msg
    puts $msg
    # Prints something like:
    #    unknown method "no.such.method": must be destroy or modulate

This is a combined example of private methods and variables.

    oo::class create LabelEqual {
        constructor {label} {
            set [my varname label] $label
        }

        private {
            # Two private declarations in the same block
            variable label

            method getLabel {} {
                return $label
            }
        }

        method equals {other} {
            # Note that this can call the private method of an
            # object other than itself. Private methods can make
            # class-internal protocols.
            expr {$label eq [$other getLabel]}
        }
    }

    oo::class create Evaluated {
        superclass LabelEqual

        # Poorly chosen variable name! Happens too easily in real life
        variable label

        constructor {expression} {
            next $expression
            set label [expr $expression]
        }

        method value {} {
            return $label
        }
    }

    set expr1 [Evaluated new {1 + 2 + 3}]
    set expr2 [Evaluated new {3 + 2 + 1}]

    puts "one is two? [$expr1 equals $expr2]"
    # Prints:
	#    one is two? 0

    puts "one=[$expr1 value] two=[$expr2 value]"
    # Prints:
	#    one=6 two=6

    puts [info vars [info object namespace $expr1]::*]
    # Prints something like:
	#    {::oo::Obj13::11 : label} ::oo::Obj13::label




This example highlights the behaviour of private variables and
`info object creationid`:

    # A simple introspection procedure for classes
    proc dumpinfo cls {
        puts "class ID of $cls is [info object creationid $cls]"
    }

    # A class with a private variable
    oo::class create Foo {
        private variable x

        constructor {} {
            # Remember, [self class] says what the class that declared
            # the current method is, i.e., ::Foo in this case.
            dumpinfo [self class]
            set x 1
        }

        method getPrivateX {} {
            # Returns the fully qualified name for 'x'
            return [my varname x]
        }
    }

    # A subclass with a normal variable with a potentially clashing name
    oo::class create Bar {
        superclass Foo

        constructor {} {
            # Ensure we call the superclass constructor first
            next

            dumpinfo [self class]
            set x 1
        }

        method getPublicX {} {
            return [my varname x]
        }
    }

    puts [Bar create abc]
    # Prints, for example:
    #    class ID of ::Foo is 11
    #    class ID of ::Bar is 12
    #    ::abc

    abc getPrivateX
    # Prints, for example:
    #    ::oo::Obj13::11 : x

    abc getPublicX
    # Prints, for example:
    #    ::oo::Obj13::x 

# Implementation

See the [`tip-500` branch](https://core.tcl.tk/tcl/timeline?r=tip-500).

# Copyright

This document has been placed in the public domain.