Version 5 of neo

Updated 2006-10-07 13:20:17 by MS

neo is for namespace ensemble objects - see NEM's Using namespace ensemble without a namespace (in fact, it uses ::). RS hacked together this little candybox as a variation on that page - yet another toy OO:

 namespace eval neo {variable version 0.1}

 proc neo::new {name _where_ args} {
     set map [dict create]
     foreach {slot = value} $args { dict set map $slot $value }
     namespace ensemble create -command $name -map $map
 }
 proc neo::slot {object slot = command args} {
     set map [config $object -map]
     dict set map $slot [linsert $args 0 $command]
     config $object -map $map
 }
 proc neo::method {object name params body} {
     set params [linsert $params 0 self]
     slot $object $name = ::apply [list $params $body ::] $object
 }
 proc neo::const val { return $val }

 interp alias {} neo::config {} namespace ensemble configure

 proc neo::delete name {rename $name {}}

#-- So far for neo the "system", the rest is tests and demos:

 proc rect {canv x0 y0 x1 y1} { #-- a constructor
    set id [$canv create rect $x0 $y0 $x1 $y1]
    set obj [neo::new $canv.rect$id where \
                 id     = [list const $id] \
                 coords = [list ::$canv coords $id]]
    neo::slot $obj canvas = const $canv
    set obj
 }
 catch {destroy .c} ;# good for repeated sourcing
 pack [canvas .c]
 set r [rect .c 20 20 100 100]
 puts "id = [$r id], coords = [$r coords]"

 neo::slot $r type = const "rectangle" ;#-- object-specific slot
 puts "$r is a [$r type]"

 neo::method $r width {} {
     lassign [$self coords] x0 y0 x1 y1
     expr {abs($x1-$x0)}
 }
 neo::method $r height {} {
     lassign [$self coords] x0 y0 x1 y1
     expr {abs($y1-$y0)}
 }
 puts height:[$r height],width:[$r width]

 neo::method $r area {} { expr {[$self width] * [$self height]} }
 puts "area = [$r area]"
 puts [neo::config $r -map]

RS So how are ensembles suitable for representing objects?

  • one object corresponds to one command
  • The popular $obj method arg... calling style is directly supported
  • dispatching and introspection are built in (in fast C code)
  • objects are easily serialized by lugging the map around (reminiscent of TOOT, also by NEM)
  • variables and methods are unified, as if we had no variables :) Getters go by name (e.g. [$self type]), setters just use neo::slot, e.g neo::slot $self type = const square
  • inheritance isn't supported yet, but that could be added (to the map)

All in all, neos are very simple yet powerful - and I like that :^)


MS proposes a small modification to ::neo::new, that allows the class methods (slot, method, const) to be available in oo manner. The first modification is taking it out of the ::neo namespace (as it does not work properly as a class method), the second is to define an unknown handler that defines the procs in ::neo as object methods:

 proc neo {name _where_ args} {
     set map [dict create]
     foreach {slot = value} $args { dict set map $slot $value }
     namespace ensemble create -command $name -map $map \
         -unknown [list ::apply [list {obj cmd args} {list ::neo::$cmd $obj}]]
 }

The novelty in here is that we can do e.g.

 % neo test where
 ::test
 % test method shout {} {puts "This is $self!"}
 % test shout
 This is ::test!

Category Object Orientation