Add a variable to a class instance at runtime: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 273: Line 273:
=={{header|Icon}} and {{header|Unicon}}==
=={{header|Icon}} and {{header|Unicon}}==
{{omit from|Icon}}
{{omit from|Icon}}
Unicon implements object environments with records and supporting procedures for creation, initialization, and methods. To modify an instance you must create a new record then copy and amend it. The procedures ''constructor'' and ''fieldnames'' are needed. This example doesn't do error checking. Here ''extend'' takes three arguments, the class instance, a list of new variable names as strings, and an optional list of new values to be assigned.
Unicon implements object environments with records and supporting procedures for creation, initialization, and methods. To modify an instance you must create a new record then copy and amend it. The procedures ''constructor'' and ''fieldnames'' are needed. This example doesn't do error checking. Here ''extend'' takes three arguments, the class instance, a list of new variable names as strings, and an optional list of new values to be assigned. As written this isn't safe from name collisions - aside from local declarations the use of a fixed constructor name uses the global name space.


''Note:'' Unicon can be translated via a command line switch into icon which allows for classes to be shared with Icon code (assuming no other incompatibilities exist).
''Note:'' Unicon can be translated via a command line switch into icon which allows for classes to be shared with Icon code (assuming no other incompatibilities exist).
Line 329: Line 329:
R_tempconstructor_1.d := 9
R_tempconstructor_1.d := 9
R_tempconstructor_1.e := 7</pre>
R_tempconstructor_1.e := 7</pre>



=={{header|J}}==
=={{header|J}}==

Revision as of 17:01, 27 December 2011

Task
Add a variable to a class instance at runtime
You are encouraged to solve this task according to the task description, using any language you may know.

Demonstrate how to dynamically add variables to an object (a class instance) at runtime.

This is useful when the methods/variables of an instance are based on a data file that isn't available until runtime. Hal Fulton gives an example of creating an OO CSV parser at An Exercise in Metaprogramming with Ruby. This is referred to as "monkeypatching" by Pythonistas and some others.

ActionScript

In ActionScript this can be done using an Object object <lang actionscript>var object:Object = new Object(); object.foo = "bar";</lang> Or by creating a dynamic class <lang actionscript>package {

   public dynamic class Foo
   {
       // ...
   }

}</lang> <lang actionscript>var foo:Foo = new Foo(); foo.bar = "zap";</lang>

Ada

Ada is not a dynamically typed language. Yet it supports mix-in inheritance, run-time inheritance and interfaces. These three allow us to achieve the desired effect, however questionably useful it could be. The example declares an interface of the class (Class). Then a concrete type is created (Base). The object E is an instance of Base. Later, at the run time, a new type Monkey_Patch is created such that it refers to E and implements the class interface per delegation to E. Monkey_Patch has a new integer member Foo and EE is an instance of Monkey_Path. For the user EE appears as E with Foo. <lang ada>with Ada.Text_IO; use Ada.Text_IO;

procedure Dynamic is

  package Abstract_Class is
     type Class is limited interface;
     function Boo (X : Class) return String is abstract;
  end Abstract_Class;
  use Abstract_Class;
  package Base_Class is
     type Base is new Class with null record;
     overriding function Boo (X : Base) return String;
  end Base_Class;
  
  package body Base_Class is
     function Boo (X : Base) return String is
     begin
        return "I am Class";
     end Boo;
  end Base_Class;
  use Base_Class;
  E : aliased Base;  -- An instance of Base
  

begin

  -- Gone run-time
  declare
     type Monkey_Patch (Root : access Base) is new Class with record
        Foo : Integer := 1;
     end record;
     overriding function Boo (X : Monkey_Patch) return String;
     function Boo (X : Monkey_Patch) return String is
     begin -- Delegation to the base
        return X.Root.Boo;
     end Boo;
     EE : Monkey_Patch (E'Access); -- Extend E
  begin
     Put_Line (EE.Boo & " with" & Integer'Image (EE.Foo));
  end;

end Dynamic;</lang> Sample output:

I am Class with 1

AutoHotkey

Works with: AutoHotkey_L

<lang AutoHotkey>e := {} e.foo := 1 </lang>

Common Lisp

This version adds a new slot only to one instance, not to the whole class.

Library: Closer to MOP

<lang lisp>(defun augment-instance-with-slots (instance slots)

 (change-class instance
               (make-instance 'standard-class
                 :direct-superclasses (list (class-of instance))
                 :direct-slots slots)))</lang>

Example:

<lang lisp>CL-USER> (let* ((instance (make-instance 'foo :bar 42 :baz 69))

               (new-slots '((:name xenu :initargs (:xenu)))))
          (augment-instance-with-slots instance new-slots)
          (reinitialize-instance instance :xenu 666)
          (describe instance))
  1. <#<STANDARD-CLASS NIL {1003AEE2C1}> {1003AEE271}>
 [standard-object]

Slots with :INSTANCE allocation:

 BAR   = 42
 BAZ   = 69
 XENU  = 666</lang>

The following REPL transcript (from LispWorks) shows the definition of a class some-class with no slots, and the creation of an instance of the class. The first attempt to access the slot named slot1 signals an error as there is no such slot. Then the class is redefined to have such a slot, and with a default value of 23. Attempting to access the slot in the preëxisting instance now gives the default value, since the slot has been added to the instance. This behavior is specified in §4.3.6 Redefining Classes of the HyperSpec.

CL-USER 57 > (defclass some-class () ())
#<STANDARD-CLASS SOME-CLASS 200BF63B>

CL-USER 58 > (defparameter *an-instance*
               (make-instance 'some-class))
*AN-INSTANCE*

CL-USER 59 > (slot-value *an-instance* 'slot1)

Error: The slot SLOT1 is missing from #<SOME-CLASS 21F59E37> (of class #<STANDARD-CLASS SOME-CLASS 200BF63B>), when reading the value.
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other options

CL-USER 60 : 1 > :a

CL-USER 61 > (defclass some-class ()
               ((slot1 :initform 23)))
#<STANDARD-CLASS SOME-CLASS 200BF63B>

CL-USER 62 > (slot-value *an-instance* 'slot1)
23


D

D is a statically compiled language, so there are some limits. This adds a new 'attribute' to a struct/class instance, of type T: <lang d>import std.stdio: writeln; import std.traits: isImplicitlyConvertible;

struct S(T) {

   T[string] data;
   template opDispatch(string name) {
       T opDispatch(Types...)(Types args)
           if (!args.length || (args.length == 1 && isImplicitlyConvertible!(Types[0],T))) {
           static if (args.length) {
               data[name] = args[0];
               return args[0];
           } else
               return data[name];
      }
  }

}

void main() {

   S!long s;
   s.foo = 1;
   writeln(s.foo());

}</lang> If the attribute name is not known at compile-time, you have to use a more normal syntax: <lang d>import std.stdio: writeln;

struct S(T) {

   T[string] data;
   alias data this;

}

void main() {

   S!long s;
   string name = "bar";
   s[name] = 2;
   writeln(s[name]);

}</lang> If the type is variable it can be used an associative array of variants, but things become more complex. Adding a name to all instances of a class can be possible.

Elena

ELENA does not support adding a variable at run-time but it can be simulated with the help of a group object <lang elena>#subject foo.

  1. class FieldContainer

{

   #field theValue.
   
   #method foo'set : anObject
   [
       theValue := anObject.
   ]
   
   #method foo'get = theValue.

}

  1. symbol Program =>

[

   #var anObject := 234.
   
   // adding a field
   anObject := anObject~FieldContainer.
   
   anObject set &foo:"bar".
   
   'program'Output << anObject << ".foo=" << anObject foo.

]. </lang>

Falcon

Classes and singleton objects have a fixed structure which cannot be changed during runtime. However falcon does have capability to add variables/functions at runtime with Prototype based objects. Below are two of the prototype objects that allow adding variables at runtime. These are arrays and dictionaries (hashes for the perl type out there).

Array: In this example we add a function (which prints out the content of the array) and a new value. While we are not technically adding a "variable", this example is presented to show similar type of functionality. <lang falcon>vect = [ 'alpha', 'beta', 'gamma' ] vect.dump = function ()

 for n in [0: self.len()]
   > @"$(n): ", self[n]
 end

end vect += 'delta' vect.dump()</lang> Output from the above: <lang falcon>0: alpha 1: beta 2: gamma 3: delta</lang> Dictionary: In this example we will add a variable through the use of an object from a bless'ed dictionary. We create a new variable called 'newVar' at runtime and assign a string to it. Additionally we assign an external, to the object, function (sub_func) to the variable 'sub'. <lang falcon>function sub_func( value )

 self['prop'] -= value
 return self.prop

end

dict = bless( [

 'prop' => 0,
 'add' => function ( value )
   self.prop += value
   return self.prop
 end ,
 'sub' => sub_func

])

dict[ 'newVar' ] = "I'm Rich In Data"</lang>

Groovy

Any Groovy class that implements "Object get(String)" and "void set(String, Object)" will have the apparent capability to add new properties. However, this capability will only work as expected with an appropriate implementation, backed by a Map object or something very much like a Map. <lang groovy>class A {

   final x = { it + 25 }
   private map = new HashMap()
   Object get(String key) { map[key] }
   void set(String key, Object value) { map[key] = value }

}</lang>

Test: <lang groovy>def a = new A() a.y = 55 a.z = { println (new Date()); Thread.sleep 5000 }

println a.x(25) println a.y (0..2).each(a.z)

println a.q</lang>

Output:

50
55
Wed Feb 23 21:33:40 CST 2011
Wed Feb 23 21:33:45 CST 2011
Wed Feb 23 21:33:50 CST 2011
null

Io

All "instance variables" (or slots in Io nomenclature) are created at runtime.

<lang io>Empty := Object clone

e := Empty clone e foo := 1</lang>

Icon and Unicon

Unicon implements object environments with records and supporting procedures for creation, initialization, and methods. To modify an instance you must create a new record then copy and amend it. The procedures constructor and fieldnames are needed. This example doesn't do error checking. Here extend takes three arguments, the class instance, a list of new variable names as strings, and an optional list of new values to be assigned. As written this isn't safe from name collisions - aside from local declarations the use of a fixed constructor name uses the global name space.

Note: Unicon can be translated via a command line switch into icon which allows for classes to be shared with Icon code (assuming no other incompatibilities exist). <lang unicon> link ximage

procedure main()

  c1 := foo(1,2)                            # instance of foo
  write("c1:\n",ximage(c1))
  c1 := extend(c1,["c","d"],[8,9])          # 2 new fields
  write("new c1:\n",ximage(c1))
  c1 := extend(c1,["e"],[7])                # 1 more
  write("newest c1:\n",ximage(c1))   

end

class foo(a,b) # dummy class end

procedure extend(instance,newvars,newvals) #: extend a class instance

  every put(f := [],fieldnames(instance))   # copy existing fieldnames
  c := ["tempconstructor"] ||| f            # new constructor    
  every put(c,!newvars)                     # append new vars
  t := constructor!c                        # new constructor
  x := t()                                  # new instance
  every x[v := !f] := instance[v]           # same as old instance  
  x.__s := x                                # new self 
  if \newvals then 
     every i := 1 to min(*newvars,*newvals) do 
        x[newvars[i]] := newvals[i]         # add new vars = values   
  return x

end</lang>

ximage.icn provides ximage to dump variable contents

Output:

c1:
R_foo__state_1 := foo__state()
   R_foo__state_1.a := 1
   R_foo__state_1.b := 2
new c1:
R_tempconstructor_1 := tempconstructor()
   R_tempconstructor_1.__s := R_tempconstructor_1
   R_tempconstructor_1.__m := R_foo__methods_1 := foo__methods()
   R_tempconstructor_1.a := 1
   R_tempconstructor_1.b := 2
   R_tempconstructor_1.c := 8
   R_tempconstructor_1.d := 9
newest c1:
R_tempconstructor_1 := tempconstructor()
   R_tempconstructor_1.__s := R_tempconstructor_1
   R_tempconstructor_1.__m := R_foo__methods_1 := foo__methods()
   R_tempconstructor_1.a := 1
   R_tempconstructor_1.b := 2
   R_tempconstructor_1.c := 8
   R_tempconstructor_1.d := 9
   R_tempconstructor_1.e := 7

J

If you assign a value to the name which references a property of a class instance, that name within that instance gets that value.

<lang j> C=:<'exampleclass' NB. this will be our class name

  V__C=: 0                   NB. ensure the class exists
  OBJ1=:conew 'exampleclass' NB. create an instance of our class
  OBJ2=:conew 'exampleclass' NB. create another instance
  V__OBJ1,V__OBJ2            NB. both of our instances exist

0

  W__OBJ1                    NB. instance does not have a W

|value error

  W__OBJ1=: 0                NB. here, we add a W to this instance
  W__OBJ1                    NB. this instance now has a W

0

  W__OBJ2                    NB. our other instance does not

|value error</lang>

JavaScript

This kind of thing is fundamental to JavaScript, as it's a prototype-based language rather than a class-based one. <lang javascript>e = {} // generic object e.foo = 1 e["bar"] = 2 // name specified at runtime</lang>

Lua

<lang lua>empty = {} empty.foo = 1</lang>

Mathematica

Mathematica doesn't rally have classes, so it doesn't have class variables. However, many rules can be applied to a single tag, so it has some aspects similar to a class. With that definition, adding a class variable is similar to adding a rule: <lang Mathematica> f[a]=1; f[b]=2; f[a]=3; ? f</lang> Output:

Global`f
f[a]=3
f[b]=2

Here, the two 'variables' can be seen under the single heading 'f'. And of course all of this is done at runtime.

Objective-C

Objective-C doesn't have the ability to add a variable to an instance at runtime. However, since Mac OS X 10.6 and iOS 3.1, it has something that can accomplish a very similar purpose, called "associative references" or "associated objects", which allow you to attach additional objects onto an object without changing its class.

You can put associative references on any object. You can put multiple ones on the same object. They are indexed by a pointer key (typically the address of some dummy variable). You use the functions objc_getAssociatedObject() and objc_setAssociatedObject to get and set them, respectively.

<lang objc>#import <Foundation/Foundation.h>

  1. import <objc/runtime.h>

char fooKey;

int main (int argc, const char *argv[]) {

   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   id e = [[NSObject alloc] init];
   // set
   objc_setAssociatedObject(e, &fooKey, [NSNumber numberWithInt:1], OBJC_ASSOCIATION_RETAIN);
   // get
   NSNumber *associatedObject = objc_getAssociatedObject(e, &fooKey);
   NSLog(@"associatedObject: %@", associatedObject);
   [e release];
   [pool drain];
   return 0;

}</lang>

Octave

Octave is dynamically typed, and can have fields added in two methods:

<lang octave> % Given struct "test" test.b=1; test = setfield (test, "c", 3); </lang>

Oz

It is not possible to add variables to instances in Oz. Every object has exactly one class and this association cannot be changed after object creation. Classes themselves are immutable.

However, classes are also first-class values and are created at runtime. Many of the tasks that are solved with "monkeypatching" in other languages, can be solved by dynamically creating classes in Oz.

<lang oz>declare

 %% Creates a new class derived from BaseClass
 %% with an added feature (==public immutable attribute)
 fun {AddFeature BaseClass FeatureName FeatureValue}
    class DerivedClass from BaseClass
       feat

 %% "FeatureName" is escaped, otherwise a new variable  %% refering to a private feature would be created

          !FeatureName:FeatureValue
    end
 in
    DerivedClass
 end
 class Base
    feat
       bar:1
    meth init
       skip
    end
 end
 Derived = {AddFeature Base foo 2}
 Instance = {New Derived init}

in

 {Show Instance.bar} %% inherited feature
 {Show Instance.foo} %% feature of "synthesized" class</lang>

To add a variable number of features and attributes, you can use Class.new.

Perl

Works with: Perl version 5.x

<lang perl>package Empty;

  1. Constructor. Object is hash.

sub new { return bless {}, shift; }

package main;

  1. Object.

my $o = Empty->new;

  1. Set runtime variable (key => value).

$o->{'foo'} = 1;</lang>


Perl 6

You can add variables/methods to a class at runtime by composing in a role. The role only affects that instance, though it is inheritable. An object created from an existing object will inherit any roles composed in with values set to those at the time the role was created. If you want to keep changed values in the new object, clone it instead. <lang perl6>class Bar { } # an empty class

my $object = Bar.new; # new instance

role a_role { # role to add a variable: foo,

  has $.foo is rw = 2;   # with an initial value of 2

}

$object does a_role; # compose in the role

say $object.foo; # prints: 2 $object.foo = 5; # change the variable say $object.foo; # prints: 5

my $ohno = Bar.new; # new Bar object

  1. say $ohno.foo; # runtime error, base Bar class doesn't have the variable foo

my $this = $object.new; # instantiate a new Bar derived from $object say $this.foo; # prints: 2 - original role value

my $that = $object.clone; # instantiate a new Bar derived from $object copying any variables say $that.foo; # 5 - value from the cloned object</lang> That's what's going on underneath, but often people just mix in an anonymous role directly using the but operator. Here we'll mix an attribute into a normal integer. <lang perl6>my $lue = 42 but role { has $.answer = "Life, the Universe, and Everything" }

say $lue; # 42 say $lue.answer; # Life, the Universe, and Everything</lang> On the other hand, mixins are frowned upon when it is possible to compose roles directly into classes (as with Smalltalk traits), so that you get method collision detection at compile time. If you want to change a class at run time, you can also use monkey patching:

<lang perl6>use MONKEY_TYPING; augment class Int {

   method answer { "Life, the Universe, and Everything" }

} say 42.answer; # Life, the Universe, and Everything</lang> This practice, though allowed, is considered to be Evil Action at a Distance.

PHP

<lang php>class E {};

$e=new E();

$e->foo=1;

$e->{"foo"} = 1; // using a runtime name $x = "foo"; $e->$x = 1; // using a runtime name in a variable</lang>

PicoLisp

In general, all instance variables in PicoLisp are dynamically created at runtime. <lang PicoLisp>: (setq MyObject (new '(+MyClass))) # Create some object -> $385605941

(put MyObject 'newvar '(some value)) # Set variable

-> (some value)

(show MyObject) # Show the object

$385605941 (+MyClass)

  newvar (some value)

-> $385605941</lang>

Pike

Pike does not allow adding variables to existing objects, but we can design a class that allows us to add variables. <lang Pike>class CSV {

   mapping variables = ([]);
   mixed `->(string name)
   {
       return variables[name];
   }
   void `->=(string name, mixed value)
   {
       variables[name] = value;
   }
   array _indices()
   {
       return indices(variables);
   }

}

object csv = CSV(); csv->greeting = "hello world"; csv->count = 1; csv->lang = "Pike";

indices(csv); Result: ({ /* 3 elements */

             "lang",
             "count",
             "greeting"
        })

</lang>

Pop11

In Pop11 instance variables (slots) are specified at class creation time and there is no way to add new slot to an instance after its class was created. However, for most practical purposes one can obtain desired effect in different way. Namely, except for a few low-level routines slots in Pop11 are accessed via getter and setter methods. Getters and setters are like ordinary methods, but are automatically defined and "know" low level details of slot access. Pop11 allows dynamic definition of methods, and one can add new methods which work as "getter" and "setter" but do not store data directly in instance. One possibility is to have one instance variable which contains a hastable (this is essentially what Perl solution is doing). Another possibility (used below) is to create na external hashtable. Adding new slots typically make sense if slot name is only known at runtine, so we create method definition (as a list) at runtime and compile it using the 'pop11_compile' procedure.

<lang pop11>lib objectclass;

define :class foo; enddefine;

define define_named_method(method, class);

   lvars method_str = method >< ;
   lvars class_str = class >< ;
   lvars method_hash_str = 'hash_' >< length(class_str) >< '_'
                             >< class_str >< '_' >< length(method_str)
                             >< '_' >< method_str;
   lvars method_hash = consword(method_hash_str);
   pop11_compile([
       lvars ^method_hash = newassoc([]);
       define :method ^method(self : ^class);
           ^method_hash(self);
       enddefine;
       define :method updaterof ^method(val, self : ^class);
           val -> ^method_hash(self);
       enddefine;
   ]);

enddefine;

define_named_method("met1", "foo"); lvars bar = consfoo(); met1(bar) =>  ;;; default value -- false "baz" -> met1(bar); met1(bar) =>  ;;; new value</lang>

PowerShell

PowerShell allows extending arbitrary object instances at runtime with the Add-Member cmdlet. The following example adds a property Title to an integer: <lang powershell>$x = 42 `

    | Add-Member -PassThru `
       NoteProperty `
       Title `
       "The answer to the question about life, the universe and everything"</lang>

Now that property can be accessed:

PS> $x.Title
The answer to the question about life, the universe and everything

or reflected:

PS> $x | Get-Member

   TypeName: System.Int32

Name        MemberType   Definition
----        ----------   ----------
CompareTo   Method       int CompareTo(System.Object value), ...
Equals      Method       bool Equals(System.Object obj), bool...
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
GetTypeCode Method       System.TypeCode GetTypeCode()
ToString    Method       string ToString(), string ToString(s...
Title       NoteProperty System.String Title=The answer to th...

While trying to access the same property in another instance will fail:

PS> $y = 42
PS> $y.Title

(which simply causes no output).

Python

<lang python>class empty(object):

   pass

e = empty()</lang>

If the variable (attribute) name is known at "compile" time (hard-coded):

<lang python> e.foo = 1</lang>

If the variable name is determined at runtime: <lang python> setattr(e, name, value)</lang>

Note: Somewhat counter-intuitively one cannot simply use e = object(); e.foo = 1 because the Python base object (the ultimate ancestor to all new-style classes) will raise attribute exceptions. However, any normal derivatives of object can be "monkey patched" at will.

Because functions are first class objects in Python one can not only add variables to instances. One can add or replace functionality to an instance. Doing so is tricky if one wishes to refer back to other instance attributes since there's no "magic" binding back to "self." One trick is to dynamically define the function to be added, nested within the function that applies the patch like so:

<lang python>class empty(object):

   def __init__(this):
       this.foo = "whatever"

def patch_empty(obj):

   def fn(self=obj):
       print self.foo
   obj.print_output = fn

e = empty() patch_empty(e) e.print_output()

  1. >>> whatever</lang>
Note: The name self is not special; it's merely the pervasive Python convention. In this example I've deliberately used this in the class definition to underscore this fact. The nested definition could use any name for the "self" object. Because it's nested the value of the object is evaluated at the time that the patch_empty() function is run and thus the function being patched in has a valid reference to the object into which it is being inserted. Other arguments could be passed as necessary. Such techniques are not recommended; however they are possible.

REBOL

<lang rebol> REBOL [ Title: "Add Variables to Class at Runtime" Author: oofoe Date: 2009-12-04 URL: http://rosettacode.org/wiki/Adding_variables_to_a_class_instance_at_runtime ]

As I understand it, a REBOL object can only ever have whatever
properties it was born with. However, this is somewhat offset by the
fact that every instance can serve as a prototype for a new object
that also has the new parameter you want to add.
Here I create an empty instance of the base object (x), then add the
new instance variable while creating a new object prototyped from
x. I assign the new object to x, et voila', a dynamically added
variable.

x: make object! [] ; Empty object.

x: make x [ newvar: "forty-two" ; New property. ]

print "Empty object modifed with 'newvar' property:" probe x

A slightly more interesting example

starfighter: make object! [ model: "unknown" pilot: none ] x-wing: make starfighter [ model: "Incom T-65 X-wing" ]

squadron: reduce [ make x-wing [pilot: "Luke Skywalker"] make x-wing [pilot: "Wedge Antilles"] make starfighter [ model: "Slayn & Korpil B-wing" pilot: "General Salm" ] ]

Adding new property here.

squadron/1: make squadron/1 [deathstar-removal-expert: yes]

print [crlf "Fighter squadron:"] foreach pilot squadron [probe pilot] </lang>

Ruby

<lang ruby>class Empty end

e = Empty.new class << e

 attr_accessor :foo

end e.foo = 1 puts e.foo # output: "1"

f = Empty.new f.foo = 1 # raises NoMethodError </lang>

"class << e" uses the singleton class of "e", which is an automatic subclass of Empty that has only this single instance. Therefore we added the "foo" accessor only to "e", not to other instances of Empty.

Slate

Slate objects are prototypes: <lang slate>define: #Empty -> Cloneable clone. define: #e -> Empty clone. e addSlotNamed: #foo valued: 1.</lang>

Smalltalk

This example is incorrect. Please fix the code and remove this message.

Details: It extends the class (adds a new instance var and new method that will exists even in brand new future instances of that class), not only the particular instance. -- The description of the problem must be reworded then, as it asks for adding variables to the class, not the instance.

<lang smalltalk>Object subclass: #Monkey

 instanceVariableNames: 'aVar'
 classVariableNames: 
 poolDictionaries: 
 category: nil !

!Monkey class methodsFor: 'new instance'! new

 | o |
 o := super new.
 o init.
 ^o

!!

!Monkey methodsFor: 'init instance'! init

 aVar := 0

! initWith: value

 aVar := value

!!

!Monkey methodsFor: 'set/get the inst var(s)'! setVar: var

 aVar := var

! getVar

 ^aVar

!!


"Create a new instance" Smalltalk at: #aMonkey put: (Monkey new) !

"set the 'original' instance var to 12" aMonkey setVar: 12 .

"let's see what's inside" aMonkey inspect .

"add a new instance var" Monkey addInstVarName: 'x'.

"let's see what's inside now" aMonkey inspect .

"let us create a new method for x" !Monkey methodsFor: 'about x'! setX: val

  x := val

! x

 ^x

!!

aMonkey setX: 10 . aMonkey inspect . (aMonkey x) printNl .</lang>

Output is:

An instance of Monkey
  aVar: 12
An instance of Monkey
  aVar: 12
  x: nil
An instance of Monkey
  aVar: 12
  x: 10
10

Tcl

Works with: Tcl version 8.6

or

Library: TclOO

The code below uses the fact that each object is implemented as a namespace, to add a time variable to an instance of summation: <lang Tcl>% package require TclOO % oo::class create summation {

  constructor {} {
      variable v 0
  }
  method add x {
      variable v
      incr v $x
  }
  method value Template:Var v {
      variable $var
      return [set $var]
  }
  destructor {
      variable v
      puts "Ended with value $v"
  }

}

summation

% set s [summation new] % # Do the monkey patch! % set [info object namespace $s]::time now now % # Prove it's really part of the object... % $s value time now %</lang> An alternative approach is to expose the (normally hidden) varname method on the object so that you can get a handle for an arbitrary variable in the object. <lang tcl>% oo::class create summation {

  constructor {} {
      variable v 0
  }
  method add x {
      variable v
      incr v $x
  }
  method value Template:Var v {
      variable $var
      return [set $var]
  }
  destructor {
      variable v
      puts "Ended with value $v"
  }

}

summation

% set s [summation new] % set s2 [summation new] % oo::objdefine $s export varname % # Do the monkey patch... % set [$s varname time] "now" % $s value time now % # Show that it is only in one object... % $s2 value time can't read "time": no such variable</lang>