Victor,
Maybe this simple study helps already
I just recovered a variation of Gustaf's study which I had used in previous projects of mine. The study below differs with respect to enforcing the "immutability" (prohibiting vs. preserving state) and adds handling of variable unsets. It slightly optimizes over the method dispatch for write traces (using ::nsf::set::var), however, it also comes at extra costs (doubled variable writes).
It can be easily extended for supporting basic introspection, kinds of "/obj/ info immutables" or the like ...
//stefan
package req nx package req nx::test package req nx::trait
::nx::Class create C { :require trait nx::traits::callback # # Make selected (by default, all currently defined) variables of an # object immutable # :public forward immutable %self traces add
# # Make selected (by default, all currently defined) variables of an # object mutable (again ...) # :public forward mutable %self traces remove
:protected method traces {op args} { foreach varName [expr {[llength $args] ? $args : [:info vars]}] { set writeCmdPrefix [list ::nsf::var::set [self] $varName [set :$varName]] set unsetCmdPrefix "$writeCmdPrefix; [:callback immutable $varName]" ::trace $op variable :$varName write "$writeCmdPrefix;#" ::trace $op variable :$varName unset "$unsetCmdPrefix;#" } } :create c1 { set :x 1 set :y 2 } }
# # c1 is mutable ... # ? {c1 eval {set :x}} 1 ? {c1 eval {set :y}} 2 ? {c1 eval {set :x 5}} 5 ? {c1 eval {set :x}} 5 ? {c1 eval {unset :y; set :y}} {can't read ":y": no such variable} ? {c1 eval {set :y 4}} 4 ? {c1 eval {set :z 3}} 3
# # c1 becomes immutable ... # c1 immutable
? {c1 eval {set :x}} 5 ? {c1 eval {set :y}} 4 ? {c1 eval {set :x 1}} 5 ? {c1 eval {set :x}} 5 ? {c1 eval {unset :y; set :y}} 4 # # Subsequent unsets are handled ... # ? {c1 eval {unset :y; set :y}} 4 ? {c1 eval {set :y 2}} 4 ? {c1 eval {set :z 3}} 3
# # c1 becomes mutable (again ...) #
c1 mutable
? {c1 eval {set :x}} 5 ? {c1 eval {set :y}} 4 ? {c1 eval {set :x 1}} 1 ? {c1 eval {set :x}} 1 ? {c1 eval {unset :y; set :y}} {can't read ":y": no such variable} ? {c1 eval {set :y 2}} 2 ? {c1 eval {set :z 3}} 3