Next

Prev

Prev-tail

Tail

Up

Chapter 7
Basic Objects, Value Model

In this section, we focus on urbiscript values as objects, and study urbiscript by-reference values model. We won’t study classes and actual objective programming yet, these points will be presented in Listing 10.

 7.1 Objects in urbiscript
 7.2 Methods
 7.3 Everything is an object
 7.4 The urbiscript values model
 7.5 Conclusion

7.1 Objects in urbiscript

An object in urbiscript is a rather simple concept: a list of slots. A slot is a value associated to a name. So an object is a list of slot names, each of which indexes a value — just like a dictionary.

 
// Create a fresh object with two slots. 
class Foo 
{ 
  var a = 42; 
  var b = "foo"; 
}; 
[00000000] Foo  

The localSlotNames method lists the names of the slots of an object (Object (Section 21.38)).

 
// Inspect it. 
Foo.localSlotNames; 
[00000000] ["a", "asFoo", "b", "type"]  

You can get an object’s slot value by using the dot (.) operator on this object, followed by the name of the slot.

 
// We now know the name of its slots. Lets see their value. 
Foo.a; 
[00000000] 42 
Foo.b; 
[00000000] "foo"  

It’s as simple as this. The inspect method provides a convenient short-hand to discover an object (Object (Section 21.38)).

 
Foo.inspect; 
[00000009] *** Inspecting Foo 
[00000010] *** ** Prototypes: 
[00000011] ***   Object 
[00000012] *** ** Local Slots: 
[00000014] ***   a : Float 
[00000015] ***   asFoo : Code 
[00000016] ***   b : String 
[00000013] ***   type : String  

Let’s now try to build such an object. First, we want a fresh object to work on. In urbiscript, Object is the parent type of every object (in fact, since urbiscript is prototype-based, Object is the uppermost prototype of every object, but we’ll talk about prototypes later). An instance of object, is an empty, neutral object, so let’s start by instantiating one with the clone method of Object.

 
// Create the o variable as a fresh object. 
var o = Object.clone; 
[00000000] Object_0x00000000 
// Check its content 
o.inspect; 
[00006725] *** Inspecting Object_0x00000000 
[00006725] *** ** Prototypes: 
[00006726] ***   Object 
[00006726] *** ** Local Slots:  

As you can see, we obtain an empty fresh object. Note that it still inherits from Object features that all objects share, such as the localSlotNames method.

Also note how o is printed out: Object_, followed by an hexadecimal number. Since this object is empty, its printing is quite generic: its type (Object), and its unique identifier (every urbiscript object has one). Since these identifiers are often irrelevant and might differ between two executions, they are often filled with zeroes in this document.

We’re now getting back to our empty object. We want to give it two slots, a and b, with values 42 and "foo" respectively. We can do this with the setSlot method, which takes the slot name and its value.

 
o.setSlot("a", 42); 
[00000000] 42 
o.inspect; 
[00009837] *** Inspecting Object_0x00000000 
[00009837] *** ** Prototypes: 
[00009837] ***   Object 
[00009838] *** ** Local Slots: 
[00009838] ***   a : Float  

Here we successfully created our first slot, a. A good shorthand for setting slot is using the var keyword.

 
// This is equivalent to o.setSlot("b", "foo"). 
var o.b = "foo"; 
[00000000] "foo" 
o.inspect; 
[00072678] *** Inspecting Object_0x00000000 
[00072678] *** ** Prototypes: 
[00072679] ***   Object 
[00072679] *** ** Local Slots: 
[00072679] ***   a : Float 
[00072680] ***   b : String  

The latter form with var is preferred, but you need to know the name of the slot at the time of writing the code. With the former one, you can compute the slot name at execution time. Likewise, you can read a slot with a run-time determined name with the getSlot method, which takes the slot name as argument. The following listing illustrates the use of getSlot and setSlot to read and write slots whose names are unknown at code-writing time.

 
function set(object, name, value) 
{ 
  // We have to use setSlot here, since we dont 
  // know the actual name of the slot. 
  return object.setSlot("x_" + name, value); 
} |; 
function get(object, name) 
{ 
  // We have to use getSlot here, since we dont 
  // know the actual name of the slot. 
  return object.getSlot("x_" + name); 
} |; 
var x = Object.clone; 
[00000000] Object_0x00000000 
set(x, "foo", 0); 
[00000000] 0 
set(x, "bar", 1); 
[00000000] 1 
x.localSlotNames; 
[00000000] ["x_bar", "x_foo"] 
get(x, "foo"); 
[00000000] 0 
get(x, "bar"); 
[00000000] 1  

Right, now we can create fresh objects, create slots in them and read them afterward, even if their name is dynamically computed, with getSlot and setSlot. Now, you might wonder if there’s a method to update the value of the slot. Guess what, there’s one, and it’s named…updateSlot (originality award). Getting back to our o object, let’s try to update one of its slots.

 
o.a; 
[00000000] 42 
o.updateSlot("a", 51); 
[00000000] 51 
o.a; 
[00000000] 51  

Again, there’s a shorthand for updateSlot: operator =.

 
o.b; 
[00000000] "foo" 
// Equivalent to o.updateSlot("b", "bar") 
o.b = "bar"; 
[00000000] "bar" 
o.b; 
[00000000] "bar"  

Likewise, prefer the ’=’ notation whenever possible, but you’ll need updateSlot to update a slot whose name you don’t know at code-writing time.

Note that defining the same slot twice, be it with setSlot or var, is an error. The slot must be defined once with setSlot, and subsequent writes must be done with updateSlot.

 
var o.c = 0; 
[00000000] 0 
// Cant redefine a slot like this 
var o.c = 1; 
[00000000:error] !!! slot redefinition: c 
// Okay. 
o.c = 1; 
[00000000] 1  

Finally, use removeSlot to delete a slot from an object.

 
o.localSlotNames; 
[00000000] ["a", "b", "c"] 
o.removeSlot("c"); 
[00000000] Object_0x00000000 
o.localSlotNames; 
[00000000] ["a", "b"]  

Here we are, now you can inspect and modify objects at will. Don’t hesitate to explore urbiscript objects you’ll encounter through this documentation like this. Last point: reading, updating or removing a slot which does not exist is, of course, an error.

 
o.d; 
[00000000:error] !!! lookup failed: d 
o.d = 0; 
[00000000:error] !!! lookup failed: d  

7.2 Methods

Methods in urbiscript are simply object slots containing functions. We made a little simplification earlier by saying that obj.slot is equivalent to obj.getSlot("slot"): if the fetched value is executable code such as a function, the dot form evaluates it, as illustrated below. Inside a method, this gives access to the target — as in C ++. It can be omitted if there is no ambiguity with local variables.

 
var o = Object.clone; 
[00000000] Object_0x0 
// This syntax stores the function in the f slot of o’. 
function o.f () 
{ 
  echo("This is f with target " + this); 
  return 42; 
} |; 
// The slot value is the function. 
o.getSlot("f"); 
[00000001] function () { 
  echo("This is f with target ".’+’(this)); 
  return 42; 
} 
// Huho, the function is invoked! 
o.f; 
[00000000] *** This is f with target Object_0x0 
[00000000] 42 
// The parentheses are in fact optional. 
o.f(); 
[00000000] *** This is f with target Object_0x0 
[00000000] 42  

This was designed this way so as one can replace an attribute, such as an integer, with a function that computes the value. This enables to replace an attribute with a method without changing the object interface, since the parentheses are optional.

This implies that getSlot can be a better tool for object inspection to avoid invoking slots, as shown below.

 
// The empty method of strings returns whether the string is empty. 
"foo".empty; 
[00000000] false 
"".empty; 
[00000000] true 
// Using getSlot, we can fetch the function without calling it. 
"".getSlot("asList"); 
[00000000] function () { 
  split("") 
}  

The asList function simply bounces the task to split. Let’s try getSlot’ing another method:

 
"foo".size; 
[00000000] 3 
"foo".getSlot("size"); 
[00000000] Primitive_0x0  

The size method of String (Section 21.58) is another type of object: a Primitive (Section 21.45). These objects are executable, like functions, but they are actually opaque primitives implemented in C ++.

7.3 Everything is an object

If you’re wondering what is an object and what is not, the answer is simple: every single bit of value you manipulate in urbiscript is an object, including primitive types, types themselves, functions, …

 
var x = 0; 
[00000000] 0 
x.localSlotNames; 
[00000000] [] 
var x.slot = 1; 
[00000000] 1 
x.localSlotNames; 
[00000000] ["slot"] 
x.slot; 
[00000000] 1 
x; 
[00000000] 0  

As you can see, integers are objects just like any other value.

7.4 The urbiscript values model

We are now going to focus on the urbiscript value model, that is how values are stored and passed around. The whole point is to understand when variables point to the same object. For this, we introduce uid, a method that returns the target’s unique identifier — the same one that was printed when we evaluated Object.clone. Since uids might vary from an execution to another, their values in this documentation are dummy, yet not null to be able to differentiate them.

 
var o = Object.clone; 
[00000000] Object_0x100000 
o.uid; 
[00000000] "0x100000" 
42.uid; 
[00000000] "0x200000" 
42.uid; 
[00000000] "0x300000"  

Our objects have different uids, reflecting the fact that they are different objects. Note that entering the same integer twice (42 here) yields different objects. Let’s introduce new operators before diving in this concept. First the equality operator: ==. This operator is the exact same as C or C ++’s one, it simply returns whether its two operands are semantically equal. The second operator is ===, which is the physical equality operator. It returns whether its two operands are the same object, which is equivalent to having the same uid. This can seem a bit confusing; let’s have an example.

 
var a = 42; 
[00000000] 42 
var b = 42; 
[00000000] 42 
a == b; 
[00000000] true 
a === b; 
[00000000] false  

Here, the == operator reports that a and b are equal — indeed, they both evaluate to 42. Yet, the === operator shows that they are not the same object: they are two different instances of integer objects, both equal 42.

Thanks to this operator, we can point out the fact that slots and local variables in urbiscript have a reference semantic. That is, when you defining a local variable or a slot, you’re not copying any value (as you would be in C or C ++), you’re only making it refer to an already existing value (as you would in Ruby or Java).

 
var a = 42; 
[00000000] 42 
var b = 42; 
[00000000] 42 
var c = a; // c refers to the same object as a. 
[00000000] 42 
// a, b and c are equal: they have the same value. 
a == b && a == c; 
[00000000] true 
// Yet only a and c are actually the same object. 
a === b; 
[00000000] false 
a === c; 
[00000000] true  

So here we see that a and c point to the same integer, while b points to a second one. This a non-trivial fact: any modification on a will affect c as well, as shown below.

 
a.localSlotNames; 
[00000000] [] 
b.localSlotNames; 
[00000000] [] 
c.localSlotNames; 
[00000000] [] 
var a.flag; // Create a slot in a. 
a.localSlotNames; 
[00000000] ["flag"] 
b.localSlotNames; 
[00000000] [] 
c.localSlotNames; 
[00000000] ["flag"]  

Updating slots or local variables does not update the referenced value. It simply redirects the variable to the new given value.

 
var a = 42; 
[00000000] 42 
var b = a; 
[00000000] 42 
// b and a point to the same integer. 
a === b; 
[00000000] true 
// Updating b wont change the referred value, 42, 
// it makes it reference a fresh integer with value 51. 
b = 51; 
[00000000] 51 
// Thus, a is left unchanged: 
a; 
[00000000] 42  

Understanding the two latter examples is really important, to be aware of what your variable are referring to.

Finally, function and method arguments are also passed by reference: they can be modified by the function.

 
function test(arg) 
{ 
  var arg.flag;  // add a slot in arg 
  echo(arg.uid); // print its uid 
} |; 
var x = Object.clone; 
[00000000] Object_0x1 
x.uid; 
[00000000] "0x1" 
test(x); 
[00000000] *** 0x1 
x.localSlotNames; 
[00000000] ["flag"]  

Beware however that arguments are passed by reference, and the behavior might not be what you may expected.

 
function test(arg) 
{ 
  // Updates the local variable arg to refer 1. 
  // Does not affect the referred value, nor the actual external argument. 
  arg = 1; 
} |; 
var x = 0; 
[00000000] 0 
test(x); 
[00000000] 1 
// x wasnt modified 
x; 
[00000000] 0  

7.5 Conclusion

You should now understand the reference semantic of local variables, slots and arguments. It’s very important to keep them in mind, otherwise you will end up modifying variables you didn’t want, or change a copy of reference, failing to update the desired one.