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)
MS proposes a small modification to ::neo::new, that allows the class methods (slot, method, config, delete) 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 {::apply {{obj cmd args} {list ::neo::$cmd $obj}}} } he novelty in here is that we can do e.g. % neo test w ::test % test method shout {} {puts "This is $self"} % test shout This is ::test % test scream invalid command name "::neo::scream" % proc ::neo::scream obj {puts "THIS IS $obj"} % test scream THIS IS ::test % test method scream {} {puts "$self does not scream"} % test scream ::test does not scream % test configure invalid command name "::neo::configure" % test config -map {scream {::apply {self {puts "$self does not scream"} ::} ::test} shout {::apply {self {puts "This is $self"} ::} ::test}} -namespace :: -prefixes 1 -subcommands {} -unknown {::apply {{obj cmd args} {list ::neo::$cmd $obj}}}
See also eos for a closely related system.