Data/program equivalence

As discussed previously, Jura is a data language and there is no difference between source code and data.  However, Jura goes one step further - there is no difference between the jura text file that represents an object instance and the code required to create that object programmatically, except that the keyword new is used to indicate that an object is being created.

Consider again the Contact example.  This automatically-created Jura text file has been created by saving a particular instance of the Contact class:

Contact{
  name={first="John" last="Winn"}
  Telephone{
    type=MOBILE
    number="+44 1234 567890"
  }
  Address:home{
    street="my street address"
    town="Cambridge"
    country="UK"
  }
}

Now let us compare the Jura and Java code for creating an identical instance of the Contact class programmatically.

Jura Java
new Contact{
  name={first="John" last="Winn"}
  Telephone{
    type=MOBILE
    number="+44 1234 567890"
  }
  Address:home{
    street="my street address"
    town="Cambridge"
    country="UK"
  }
}

Contact c = new Contact();
c.getName().setFirst("John");
c.getName().setLast("Winn");
Telephone t = new Telephone();
t.setType(Telephone.MOBILE);
t.setNumber("+44 1234 567890");
c.add(t);
Address a = new Address();
a.setName("home");
a.setStreet("my street address");
a.setTown("Cambridge");
a.setCountry("UK");
c.add(a);

The Jura code looks exactly the same as the text file above except for the addition of the new keyword.  Apart from being far more readable than the Java equivalent on the right, this feature of Jura means that saved objects can be used as the basis for creating code to generate similar objects dynamically.  This ability is the key to creating Programmatic Design Patterns, generic classes and transforms.

Structure operators

The elegance of representing structured data in Jura is achieved by the use of structure operators.  These include the open and create operators.  The open operator looks like this:

object={
  {* Nested expressions *}
}

The nested expressions are resolved as if they were written in a method of the class of object, except that the access permissions of the current class apply.  Typically, this will mean that only public properties or methods of object can be accessed.   To allow access to variables outside of the open operator, the popup operator $ is provided.  Here is an example of its use, with the equivalent Java code for comparison:

Jura Java
Local:name{type=String}="MyObject"
object={
  name = $name
}
String name = "MyObject";

object.setName(name);

Here, a local variable called name is declared.  The name property of object is then set to have the value of the local variable.  The popup operator  is necessary to distinguish between the property of the opened variable and the local variable, both of which are called name.

The create operator has this syntax:

new Type{
  {* Nested expressions *}
}

and is equivalent to applying an open operator on a new instance of the type and inserting initialisation and validation of  the instance:

(new Type())={
  initialise()
  {* Nested expressions *}
  validate()
}

It follows that Jura does not use constructors (although Java constructors can be called if necessary).  Instead, a parameter-less initialise() method can be implemented to initialise an instance (e.g. perform memory allocation), then a number of expressions executed (e.g. setting properties, running public methods) and finally the state of the instance can be validated by providing a validate() method.  These methods will only be called if the instance implements the Initialisable and Validatable interfaces, which are examples of abilities.

Omitting new from a create expression, is equivalent to applying the popup operator to that expression.

An example of using data/program equivalence to create a simple template

We can now write a parameterised method to return a Contact instance, given a first name, last name and mobile number. The code looks like a template with parameters marked with the pop-up operator $. As a reminder, this operator means 'evaluate the following expression as though outside the create statement'.

Method:createContact{returns=Contact
  Par:first{type=String}
  Par:last{type=String}
  Par:mobile{type=String}
  //
  Return{
    new Contact{
      name={first=$first last=$last}
      Telephone{
        type=MOBILE
        number=$mobile
      }
    }
  }
}