Urbi 2 Technical Preview
With Urbi 1 Gostai introduced a revolutionary means to program robots: a domain specific scripting language that brings together the efficiency of C++, and the flexibility of a dynamic language. Urbi 1 was designed pragmatically, based on user requests and feedback. That’s how Urbi 1 was made useful, and how it met its users expectations. Yet, because of this iterative design process, it suffers a number of limitations.
To address these issues, Gostai launched the Urbi 2 project months ago. We are happy to announce that Urbi 2 is up and running. It will be made available for technical preview in the near future. In the meanwhile, let us show you some of the changes we made in Urbi. For a start, we will focus on the changes of the Object-Orientation support in Urbi, later articles will cover other facets of the Urbi 2 system.
What follows is implemented, runs, and can be done at home in total safety.
Full Object Orientation Support
- Everything is an object.
In the 1.x series there were several kind of entities: numbers, lists, strings, images, etc. and objects. In the 2.x series, everything is an object, including the atomic values. For instance “foo” evaluates into an object that derives from String.
- Objects are dictionaries of slots.
Objects are implemented as associative arrays: a mapping from symbols to objects.
- The distinction between value and function is blurred.
From a software engineering point of view, the client (i.e., the “caller”) should not know how the provider (i.e., the “callee”) is implemented. In particular, it is an implementation detail whether object.feature is an attribute, or a function taking no argument. Therefore, parentheses are not needed to invoke functions that do not have argument (other that self)# 0-ary functions and attributes should behave equally. var feature = 42; [11648000] 42 feature; [11680000] 42 feature(); [11712000] 42 feature(42); [11776000:error] !!! input.u:8.1-11: expected 0 arguments, given 1 feature = function () { 42 }; [11872000] function () { 42 } feature; [12000000] 42 feature(); [12128000] 42 feature(42); [12192000:error] !!! input.u:10.1-11: expected 0 arguments, given 1feature = function (x) { x }; [12256000] function (x) { x } feature; [12288000:error] !!! input.u:26.1-7: expected 1 arguments, given 0 feature(); [12288000:error] !!! input.u:28.1-9: expected 1 arguments, given 0 feature(42); [12384000] 42
- Functions are first-class citizens.
In other words, functions are objects and can be manipulated like any other type of object. For instance you may copy a function from an object to another. Be careful though that by default code is evaluated, see the following example.var counter_ = 0; [09856000] 0 var counter = function () { counter_ += 1; }; [09952000] function () { counter_ . '+='(1); } counter; [10080000] 1 counter; [10208000] 2 var counter2 = counter; [10368000] 3 counter2; [10400000] 3 counter2; [10432000] 3 var counter3 = getSlot("counter"); [10528000] function () { counter_ . '+='(1); } counter3; [10656000] 4 counter3; [10784000] 5 counter; [10912000] 6
- Arbitrary identifiers.
Identifiers can be composed of any sequence of characters enclosed between ‘, but some sequences behave magically.- Regular identifiers.
Traditional identifiers (MyObject, my_function_2, SomeVariableName etc.) need no quotes. - Traditional connectives.
Connectives such as +, *, <= and so forth, when used unquoted behave as usual (e.g., 1 < 2), but between quotes, they behave like regular function names (e.g., 1.’<’(2)).
- Regular identifiers.
- Fewer primitives, more expressive power.
The basic connectives, such as +, *, <= and so forth, are regular functions that the user can freely define. For instance Person.’<’ = function (rhs) { name < rhs.name } define the < between Person.
- Objects can be arbitrarily nested.
The previous limitations of the language are alleviated, and the . connective can now be freely used. For instance:robot.leg("rear-left").joint("knee").value
- Name spaces.
There is no global name space in the Urbi language: there are no global variables or functions in the traditional sense. Rather,
the Urbi world is a network of objects that use each other (either via inheritance, or via aggregation). To reach a particular object, for instance Object from which all the objects derive, one uses a series of local names starting from the current object.For instance true is defined as a member of Object.Constants, which is a prototype of Object (i.e., Object “derives” from Object.Constants). Since every object derives from Object, the true is available from anywhere.
- Lobbies.
Sessions with the Urbi server, via files or via network connections, are handled via “lobbies”. Lobbies implement the local scope: variables introduced during the interaction are only visible from this lobby. Nevertheless, because lobbies are connected to the rest of the Urbi network of objects, the user can introduce new features in the common space, for instance by extending Object. The user may also provide access to
his/her own lobby by giving it a name in an object that is reachable from the other lobbies.
- An object can have any number of prototypes.
Multiple inheritance is fully supported. This allows to factor common code. For instance the object Orderable provides implementations of ordered comparators in terms of each other (e.g., function ‘>=’ (rhs) { rhs < self }), and Comparable relates == and !=. If your
object provides == and <, then running object.addProto(Comparable);object.addProto(Orderable); provides the others.
There is much more to come, expect new posts in the near future and stay tuned!
Posted: February 22nd, 2008 under Features Preview.
Comments: none