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 Side-by-Side Diffs Ignore Whitespace Patch

Changes to tip/500.md.

   117    117   In particular:
   118    118   
   119    119       oo::class create Top {
   120    120           method Foo {} {
   121    121               puts "This is Top::Foo for [self]"
   122    122           }
   123    123       }
          124  +
   124    125       oo::class create Super {
   125    126           superclass Top
          127  +
   126    128           private method Foo {} {
   127    129               puts "This is Super::Foo for [self]"
   128    130           }
          131  +
   129    132           method bar {other} {
   130    133               puts "This is Super::bar for [self]"
   131    134               my Foo
   132    135               [self] Foo
   133    136               $other Foo
   134    137           }
   135    138       }
          139  +
   136    140       oo::class create Sub {
   137    141           superclass Super
          142  +
   138    143           method Foo {} {
   139    144               puts "This is Sub::Foo for [self]"
   140    145               next
   141    146           }
          147  +
   142    148           private method bar {other} {
   143    149               error "This should be unreachable"
   144    150           }
          151  +
   145    152           method grill {} {
   146    153               puts "This is Sub::grill for [self]"
   147    154               my Foo
   148    155           }
   149    156       }
          157  +
   150    158       Sub create obj1
   151    159       Sub create obj2
   152    160       obj1 bar obj2
   153    161       obj1 grill
   154    162   
   155    163   is expected to print out:
   156    164   
................................................................................
   172    180   Private methods are created calling **oo::define** (and **oo::objdefine**, and
   173    181   via the constructor of **oo::class**) like this:
   174    182   
   175    183       oo::define TheClass {
   176    184           private method foo {...} {
   177    185               ...
   178    186           }
          187  +
   179    188           private forward bar  SomeOtherCommand
   180    189       }
   181    190   
   182    191   At the C API level, private methods can be directly created by setting the
   183    192   *flags* parameter (called *isPublic* in older versions of the API) of
   184    193   `Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant
   185    194   `TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private
................................................................................
   200    209   <dd>Reports the truly private methods only.</dd></dl>
   201    210   
   202    211   The **-scope** option causes the **-all** and **-private** options to be
   203    212   ignored.
   204    213   
   205    214   # Detailed Proposal: Variables
   206    215   
   207         -Variables are more complex, as they necessarily can be named by other commands
   208         -(e.g., so that they can be **vwait**ed upon). This requires them to be named
          216  +Variables are more complex, conceptually, as they necessarily can be named by
          217  +other commands (e.g., so that they can be **vwait**ed upon or used with Tk
          218  +widgets; the key underlying operation in both of these cases is that they must
          219  +support traces) and so cannot be completely separated from the rest of Tcl.
          220  +This requires them to be named
   209    221   by compounding the user-supplied name (to reduce confusion) and a unique
   210    222   identifier that identifies the particular context of the name. Now, one of the
   211    223   trickier things about TclOO is that it does not provide fixed names for
   212    224   objects; the **rename** command is supported. Because of that, the
   213    225   fully-qualified name of an object or class, while unique, is not a stable
   214    226   identifier. The full name of the backing namespace *is* stably-named (as
   215    227   namespaces cannot be renamed), but only has a unique name when the
................................................................................
   236    248   created by declaration; that's up to an appropriate method to actually do (and
   237    249   the variable can be created as normal scalar, array or link). When a method
   238    250   runs in the relevant class, the class's variable resolver will map the private
   239    251   variable in with the user-expected name.
   240    252   
   241    253   The **variable** and **varname** methods of **oo::object** will be aware of
   242    254   the mechanism, respecting what names are visible in the context that invokes
   243         -them.
          255  +them. Thus, if **varname** is called from a context that would be able to see
          256  +the private variable, `foobar`, it will return the fully qualified name of the
          257  +Tcl variable variable in the target object's namespace that acts as the
          258  +storage of that variable; when the declaring class has creation ID `123` and
          259  +the instance of that class has namespace `::oo::Obj456`, the result of
          260  +`my varname foobar` will be `::oo::Obj456::123 : foobar`; the parts of that
          261  +name are:
          262  +
          263  + * “`::oo::Obj456`” — Fully-qualified instance namespace name; private
          264  +   variables are still ultimately variables that exist on object instances.
          265  + * “`::`” — Namespace separator.
          266  + * “`123`” — Declaring context ID, from the class that holds the definition of
          267  +   the private variable.
          268  + * “` : `” — Private variable separator; a space, a colon, and another space.
          269  +   This is chosen to be very unlikely to be in user scripts by accident (as
          270  +   variable names with spaces and single colons are traditionally known to be
          271  +   awkward to use explicitly).
          272  + * “`foobar`” — User-supplied variable name fragment.
          273  +
          274  +Similarly, the **variable** method can be used to bring the private variable
          275  +into scope; this would not normally be necessary, but is useful when the usual
          276  +resolution rules for the variable are suspended (such as when a variable with
          277  +that name is given as a formal argument to a constructor).
   244    278   
   245    279   ## Creation and Introspection
   246    280   
   247    281   A private variable mapping is created like this:
   248    282   
   249    283       oo::define Foo {
   250         -        private variables x y
          284  +        private variable x y
          285  +
   251    286           method foo {} {
   252    287               set x 1
   253    288               set y(z) 2
   254    289           }
   255    290       }
   256    291   
   257    292   That is, the **private** declaration command described above (for methods)
................................................................................
   272    307   
   273    308   A supporting introspector is also added, **info object creationid**, which
   274    309   returns the creation ID of any existing object. It also applies to classes.
   275    310   Again, note that creation IDs are _always_ system-allocated and are _never_
   276    311   guaranteed to be unique between interpreters, either in multiple processes or
   277    312   in the same thread or process; they are only ever locally unique.
   278    313   
   279         -# Example
          314  +# Examples
          315  +
          316  +This example shows a private variable linked to a Tk entry:
          317  +
          318  +    oo::class create Editable {
          319  +        variable w;            # Allow subclasses to see this
          320  +        private variable val;  # Hide this from subclasses
          321  +
          322  +        constructor {widgetName} {
          323  +            set val {}
          324  +            set w [entry $widgetName -textvariable [my varname val]
          325  +        }
          326  +
          327  +        method value {args} {
          328  +            if {[llength $args] == 0} {
          329  +                return $val
          330  +            } elseif {[llength $args] == 1} {
          331  +                lassign $args val
          332  +                return
          333  +            }
          334  +            return -code error "wrong # args: ..."
          335  +        }
          336  +
          337  +        method trace {callbackLambda} {
          338  +            trace add variable val write [list apply $callbackLambda]
          339  +        } 
          340  +    }
          341  +
          342  +    Editable create field .e
          343  +    field trace {args {
          344  +        puts "field is now [field value]"
          345  +    }}
          346  +    field value "Set the contents"
          347  +
          348  +Ths is an example of private methods. It shows how private methods can be used
          349  +to manage the complexity of a method without making its API (as exposed to
          350  +either subclasses or the rest of Tcl) overly complex.
          351  +
          352  +    oo::class create Modulator {
          353  +        # The exported interface to the class
          354  +        method modulate {args} {
          355  +            try {
          356  +                foreach value $args {
          357  +                    my modulateOne $value
          358  +                }
          359  +            } on error {msg opts} {
          360  +                tailcall return {*}[my reworkError $msg $opts]
          361  +            }
          362  +        }
          363  +
          364  +        private {
          365  +            # Do something with the single value; practical code would do more
          366  +            method modulateOne {value} {
          367  +                puts "this would modulate with $value"
          368  +                if {$value > 5} {
          369  +                    error "this is bad"
          370  +                }
          371  +            }
          372  +
          373  +            # Simple way to hide the internal structure of the class from errors
          374  +            method reworkError {msg opts} {
          375  +                dict set opts -errorinfo "problem with modulating: $msg"
          376  +                list -options $opts $msg
          377  +            }
          378  +        }
          379  +    }
          380  +
          381  +    set m [Modulator new]
          382  +
          383  +    catch {$m modulate 4 5 6 7 8} -> opts
          384  +    puts [dict get $opts -errorinfo]
          385  +    # Prints something like:
          386  +    #    this would modulate with 4
          387  +    #    this would modulate with 5
          388  +    #    this would modulate with 6
          389  +    #    problem with modulating: this is bad
          390  +    #        invoked from within
          391  +    #    "::oo::Obj15 modulate 5 6 7 8"
          392  +
          393  +    catch {$m no.such.method} msg
          394  +    puts $msg
          395  +    # Prints something like:
          396  +    #    unknown method "no.such.method": must be destroy or modulate
          397  +
          398  +This is a combined example of private methods and variables.
   280    399   
   281    400       oo::class create LabelEqual {
   282    401           constructor {label} {
   283    402               set [my varname label] $label
   284    403           }
          404  +
   285    405           private {
   286    406               # Two private declarations in the same block
   287    407               variable label
          408  +
   288    409               method getLabel {} {
   289    410                   return $label
   290    411               }
   291    412           }
          413  +
   292    414           method equals {other} {
          415  +            # Note that this can call the private method of an
          416  +            # object other than itself. Private methods can make
          417  +            # class-internal protocols.
   293    418               expr {$label eq [$other getLabel]}
   294    419           }
   295    420       }
   296    421   
   297    422       oo::class create Evaluated {
   298    423           superclass LabelEqual
          424  +
   299    425           # Poorly chosen variable name! Happens too easily in real life
   300    426           variable label
          427  +
   301    428           constructor {expression} {
   302    429               next $expression
   303    430               set label [expr $expression]
   304    431           }
          432  +
   305    433           method value {} {
   306    434               return $label
   307    435           }
   308    436       }
   309    437   
   310    438       set expr1 [Evaluated new {1 + 2 + 3}]
   311    439       set expr2 [Evaluated new {3 + 2 + 1}]
          440  +
   312    441       puts "one is two? [$expr1 equals $expr2]"
   313         -    # Prints: one is two? 0
          442  +    # Prints:
          443  +	#    one is two? 0
          444  +
   314    445       puts "one=[$expr1 value] two=[$expr2 value]"
   315         -    # Prints: one=6 two=6
          446  +    # Prints:
          447  +	#    one=6 two=6
          448  +
   316    449       puts [info vars [info object namespace $expr1]::*]
   317         -    # Prints something like: {::oo::Obj13::11 : label} ::oo::Obj13::label
   318         -    catch {$expr2 getLabel} msg
   319         -    puts $msg
   320         -    # Prints: unknown method "getLabel": must be destroy, equals or value
          450  +    # Prints something like:
          451  +	#    {::oo::Obj13::11 : label} ::oo::Obj13::label
          452  +
          453  +This example highlights the behaviour of private variables and
          454  +`info object creationid`:
          455  +
          456  +    # A simple introspection procedure for classes
          457  +    proc dumpinfo cls {
          458  +        puts "class ID of $cls is [info object creationid $cls]"
          459  +    }
          460  +
          461  +    # A class with a private variable
          462  +    oo::class create Foo {
          463  +        private variable x
          464  +
          465  +        constructor {} {
          466  +            # Remember, [self class] says what the class that declared
          467  +            # the current method is, i.e., ::Foo in this case.
          468  +            dumpinfo [self class]
          469  +            set x 1
          470  +        }
          471  +
          472  +        method getPrivateX {} {
          473  +            # Returns the fully qualified name for 'x'
          474  +            return [my varname x]
          475  +        }
          476  +    }
          477  +
          478  +    # A subclass with a normal variable with a potentially clashing name
          479  +    oo::class create Bar {
          480  +        superclass Foo
          481  +
          482  +        constructor {} {
          483  +            # Ensure we call the superclass constructor first
          484  +            next
          485  +
          486  +            dumpinfo [self class]
          487  +            set x 1
          488  +        }
          489  +
          490  +        method getPublicX {} {
          491  +            return [my varname x]
          492  +        }
          493  +    }
          494  +
          495  +    puts [Bar create abc]
          496  +    # Prints, for example:
          497  +    #    class ID of ::Foo is 11
          498  +    #    class ID of ::Bar is 12
          499  +    #    ::abc
          500  +
          501  +    abc getPrivateX
          502  +    # Prints, for example:
          503  +    #    ::oo::Obj13::11 : x
          504  +
          505  +    abc getPublicX
          506  +    # Prints, for example:
          507  +    #    ::oo::Obj13::x 
   321    508   
   322    509   # Implementation
   323    510   
   324    511   See the [`tip-500` branch](https://core.tcl.tk/tcl/timeline?r=tip-500).
   325    512   
   326    513   # Copyright
   327    514   
   328    515   This document has been placed in the public domain.