IndexNextUpPreviousUrbi SDK 3.0.0

Chapter 4
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 7.

 4.1 Objects in urbiscript
 4.2 Methods
 4.3 Everything is an object
 4.4 The urbiscript values model
 4.5 Conclusion

4.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).

 
// 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. Let’s 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).

 
Foo.inspect(); 
[00000000] *** Inspecting Foo 
[00000000] *** ** Prototypes: 
[00000000] ***   Object 
[00000000] *** ** Local Slots: 
[00000000] ***   a : Float 
[00000000] ***   asFoo : Code 
[00000000] ***   b : String 
[00000000] ***   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 setSlotValue method, which takes the slot name and its value.

 
o.setSlotValue("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.setSlotValue("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 getSlotValue method, which takes the slot name as argument. The following listing illustrates the use of getSlotValue and setSlotValue to read and write slots whose names are unknown at code-writing time.

 
function set(object, name, value) 

  // We have to use setSlotValue here, since we don’t 
  // know the actual name of the slot. 
  object.setSlotValue("x_" + name, value) 
}|; 
 
function get(object, name) 

  // We have to use getSlotValue here, since we don’t 
  // know the actual name of the slot. 
  object.getSlotValue("x_" + name) 
}|; 
 
var x = Object.clone(); 
[00000000] Object_0x42342448 
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 getSlotValue and setSlotValue. 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 setSlotValue or var, is an error. The slot must be defined once with setSlotValue, and subsequent writes must be done with updateSlot.

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

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

 
o.localSlotNames(); 
[00000000] ["a", "b", "c"] 
o.removeLocalSlot("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  

4.2 Methods

Methods in urbiscript are simply object slots containing functions. Using obj.slot if slot contains a function will return the function itself. Use obj.slot() to evaluate the function instead.

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_0x00000001 
// 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.getSlotValue("f"); 
[00000001] function () { 
  echo("This is f with target ".’+’(this)); 
  return 42; 
} 
// Huho, no parenthesis, no call. 
o.f; 
[00000001] function () { 
  echo("This is f with target ".’+’(this)); 
  return 42; 
} 
o.f(); 
[00000000] *** This is f with target Object_0x00000001 
[00000000] 42  

Let’s use getSlotValue to introspect some functions:

 
// The ’asList’ method of strings returns the characters in a list. 
"foo".asList(); 
[00003447] ["f", "o", "o"] 
// Using getSlot, we can fetch the function without calling it. 
"".getSlotValue("asList"); 
[00000000] function () { split("") }  

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

 
"foo".isBlank(); 
[00000000] false 
"foo".getSlotValue("isBlank"); 
[00000000] Primitive_0x422f0908  

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

4.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.

4.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 won’t 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_0x00000001 
x.uid; 
[00000000] "0x00000001" 
test(x); 
[00000000] *** 0x00000001 
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 wasn’t modified 
x; 
[00000000] 0  

4.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.