Add a variable to a class instance at runtime

From Rosetta Code
Revision as of 18:55, 5 June 2009 by rosettacode>Briantrice (Slate implementation)
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.

This demonstrates how to dynamically add variables to a class instance at runtime. This is useful when the methods/variables 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. It's possible in most dynamic OO languages such as Python, Ruby, and Smalltalk.

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

Io

Empty := Object clone
e := Empty clone
e foo := 1 

JavaScript

e = {}       // generic object
e.foo = 1

Perl

Works with: Perl version 5.x
package Empty;

# Constructor. Object is hash.
sub new { return bless {}, shift; }

package main;

# Object.
my $o = Empty->new;

# Set runtime variable (key => value).
$o->{'foo'} = 1;

PHP

class E {};

$e=new E();

$e->foo=1;

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.

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

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()
  # >>> 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.

Ruby

<lang ruby> class Empty

end
e = Empty.new
e.instance_variable_set("@foo", 1)
e.instance_eval("class << self; attr_accessor :foo; end")
puts e.foo</lang>

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.

<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
Works with: Tcl version 8.5

and the TclOO package

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>% 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>