[NEM] ''6 Oct 2006'': While contemplating all things [OO] and [TIP] 257 [http://tip.tcl.tk/257], I came across an interesting interaction between features available in Tcl 8.5. It is possible to use [namespace ensemble] without needing any namespace to back it up! In particular, the -map option can be used to model simple slot-based objects. Together with the [apply] command, methods can then be added. To demonstrate, let's consider writing a simple stateless object to represent a rectangle on a canvas: proc rect {canv x0 y0 x1 y1} { set id [$canv create rect $x0 $y0 $x1 $y1] namespace ensemble create -command $canv.rect$id \ -map [dict create id [list const $id] \ coords [list $canv coords $id]] } proc const val { return $val } We now have a simple constructor that creates a rectangle on a canvas and creates an object command for that rectangle, with two ''slots'' to get the canvas id and coordinates associated with the rectangle: pack [canvas .c] set r [rect .c 20 20 100 100] puts "id = [$r id], coords = [$r coords]" These slots are just entries in the -map of the ensemble that alias to the "const" command to simply return their value. Note that ''no namespace is involved at all!'' ([DKF]: Actually, the namespace involved is the global namespace, but getting rid of that is equivalent to deleting the interpreter. [NEM]: Right, but it's not actually used for anything?) We can write a general method for creating slot aliases on these objects: proc alias {object slot = command args} { set map [namespace ensemble configure $object -map] dict set map $slot [linsert $args 0 $command] namespace ensemble configure $object -map $map } We can now add extra slots to the rectangle object: alias $r type = const "rectangle" puts "$r is a [$r type]" Using anonymous functions via [apply] in 8.5, we can even add new methods to the object. These are simply aliases to an anonymous function. We add a "self" parameter and arrange for it to be filled with the object command: proc method {object name params body} { set params [linsert $params 0 self] alias $object $name = ::apply [list $params $body ::] $object } method $r width {} { lassign [$self coords] x0 y0 x1 y1 expr {abs($x1-$x0)} } method $r height {} { lassign [$self coords] x0 y0 x1 y1 expr {abs($y1-$y0)} } We can even add method and alias as methods on the object itself: alias $r method = ::method $r alias $r alias = ::alias $r $r method area {} { expr {[$self width] * [$self height]} } puts "area = [$r area]" We could add mutable slots by creating a slot alias that can rewrite itself. This seems to be a fascinating new way of creating relatively ''lightweight'' objects, benefiting from the fast [namespace ensemble] mechanism, without having the overhead of an actual namespace. ---- [RS] proposes to call such "namespace ensemble objects" just "[neo]" :^) Also, in the chat we brain-stormed a bit more. Here's some sugar for neo creation: proc create {name map} {namespace ensemble create -command $name -map $map} The ''rect'' example then becomes proc rect {canv x0 y0 x1 y1} { set id [$canv create rect $x0 $y0 $x1 $y1] create $canv.rect$id [dict create \ id [list const $id] \ coords [list $canv coords $id]] } Introspection goes with ''namespace ensemble configure'': % namespace ensemble configure ::.c.rect1 -map {width {::apply {self { lassign [$self coords] x0 y0 x1 y1 expr {abs($x1-$x0)} } ::} ::.c.rect1} height {::apply {self { lassign [$self coords] x0 y0 x1 y1 expr {abs($y1-$y0)} } ::} ::.c.rect1} id {::const 1} coords {::.c coords 1} type {::const rectangle}} -namespace :: -prefixes 1 -subcommands {} -unknown {} [NEM]: I've added a little sugar to the alias method, to separate name and definition more clearly. We can further sugar "create" into a cute little object constructor. proc object {name _where_ args} { set map [dict create] foreach {slot = value} $args { dict set map $slot $value } namespace ensemble create -command $name -map $map } Which allows to leave off the dict create. The original proc can then become: proc rect {canv x0 y0 x1 y1} { set id [$canv create rect $x0 $y0 $x1 $y1] set obj [object $canv.rect$id where \ id = [list const $id] \ coords = [list $canv coords $id]] $obj alias canvas = const $canv } (Using alias for one of the initial slots too, for variety). ---- [RS] Nice - this is really getting to be something :^) One question, though - how would one get rid of an ensemble object? [namespace ensemble] indicates no way to withdraw a -command... [NEM] Hmmm... [rename] $obj {} ? ---- [[[Category Object Orientation]|[Category Example]]]