Ballerina Object type and all you need to know

Rajith Vitharana
Ballerina Swan Lake Tech Blog
6 min readJul 12, 2018

Ballerina is a general purpose programming language which is being developed by WSO2, More details here.

Ballerina possesses a pretty amazing type system based on set theory, the best I have seen so far.

You can simply define your own custom type with below syntax.

TYPE identifier typeDescriptor;

Although this notation can be used to define many number of custom types, this blog post is only explaining about the type called object type.

Object type is a one of type descriptor that you can use to create your own types behavioral type. So object types can have fields as well as member functions to give it the behavioral aspects. Object type can be described as follows

objectTypeDescriptor:
(visibilityMod typeName id)*
constructorFunction?
(visibiltyMod memberFunction)*

Visibility Modifiers

If we talk about visibility modifiers first, there are three types of visibility modifiers, which are

  • public — visible everywhere
  • private — visible only within the same object
  • no modifier — visible only within same package.

Important thing to note is that whole object definition itself can have a visibility modifier as well, in which you cannot use private visibility modifier, there you only have public and protected(which is no modifier).

example object definition would be as follows

As you can see in the above example, you can define initial values for these fields as well. These initial values can be either simple literals or complex expressions.

Object Constructor.

you can define a constructor in the object so that it can be used to initialize an instance of the object. Constructor syntax would look like follows

PUBLIC? NEW (fieldList?) invokableBody?

This is almost same as a function except few differences, those are, name of this constructor must be “new”, it only has two visibility modifiers, public and protected(means no modifier) and it can refer object fields directly as its parameters.

And if there is no constructor defined in the object, then internally we inject a default constructor with no parameters to the object.

(from this point onward object constructor with no parameters will be mentioned as default constructor in this post)

example object with these features would be as follows.

As you can see in the above sample, we are referring “name” and “age” fields in the object as parameters in the constructor. And also to make this bit complex, I have used a function call to set the default value for “email” field in the object.

If I explain bit about internals, it’s as follows,

first of all we are using this constructor to initialize object instances. So when we have a statement as following in the code,

Person andrew = new Person("Andrew", "England");

what we do internally is call the constructor of the given object definition. So to initialize values like email, what we have to do internally is simply inject an assignment statement to the constructor body like below

public new (name, age = 50, string country) {   
email = getEmail(); //injected assignment statement
address = country;
}

We use this same concept when constructor has parameters which directly refer object fields.

Object member functions

objects can have member functions attached to the object definition which provides behavioral aspects to the object definition. These member functions can be defined in two ways.

1. Define the member function within the object itself

2. Declare the member function signature inside the object and define it’s definition(along with function body) out side of the object

for the second option to work, you need to first declare the function signature inside the object definition. (with this we can keep the object definition like a API and prevent it from getting cluttered with long function definitions). Then you need to define the function outside of the object definition with a signature like below

FUNCTION typeName::Identifier(paramList?) ....

Note the “typeName::” part which we use to identify the type to which this function should be bound.

These member functions also have the same visibility modifiers as object fields. And also you need to add the visibility modifiers only to the function declaration which resides inside the object definition, member functions outside of the object shouldn’t have the visibility modifier specified because it is already specified in the function declaration inside the object. Below is a sample object with member functions. (Both outside and inside of the object)

Object instantiation

There are three ways to create an instance of an object

  1. using just “new” keyword
Person p = new;

for this to work, object definition of Person should have a default constructor(or a constructor with default-able parameters). No constructor in the object definition should also work since we inject a default constructor in that case.

2. using “new” keyword with just parenthesis

Person p = new ();

same as case 1, you need a default constructor for this to work

3. using “new” keyword along with type name

Person p = new Person();

actually case 1, 2 are just syntactic sugar for case 3, internally what happens is case 3

This becomes really useful in cases like where you need to initialize a different type of object at the right-hand side or pass parameters to constructor etc.

what??? different type of object at right hand side??

Yes, you heard that correct, ballerina has a structural type system. So you can initialize any other type of object and assign that to “Person p” as long as right-hand side type and “Person” type(which is left-hand side type) are structurally equivalent. Ballerina will type check this so that right-hand side is structurally equivalent to the left-hand side.

Structural equivalence

There are some rules that governs this structural equivalence behavior in ballerina. Although that is a different topic to talk about, I’ll explain the most basic rule for two object types to become structurally equivalent.

For two object types to be structurally equivalent, the Right Hand Side type must have at least same fields and same member functions as the Left Hand Side type

With this basic rule, below example illustrates two object types which are structurally equivalent. And it also shows how to initialize and assign those two equivalent types to each other.

You can see in the above code sample that Employee object type is structurally equivalent to the Person object type. Employee object type has all the fields and the member functions of the Person object type and some more as well. So as you can see in line number 31 in the example, you can create an Employee object instance and assign that to Person type variable.

Object defaultability

Ballerina tries to prevent null pointer reference issues. For that, every variable declaration will automatically get defaulted. For example, if someone declares a variable as follows

Person p;

then internally we assign the default value for the variable “p” like below

Person p = new;

For this to work, type Person should be defaultable. So how do we determine whether an object type is defaultable or not?

That’s pretty simple, for an object to be defaultable, all its fields should be defaultable and object should have a default constructor. Here “default constructor” doesn’t mean that the constructor shouldn’t have parameters, it just means that constructor should be invokable without parameter argument values. For example below sample object is defaultable

Although constructor in above sample have parameters specified, those parameters are defaultable parameters, so you can initialize above object by simply calling the constructor without argument values as below

Person p = new Person();

Anonymous Objects

Since this is a type descriptor, you don’t need to always define the object type as a type definition as well. You can simply use this as an in-line anonymous type as well. Since ballerina has a structural type system, you can just use in-line object types same as defined object types,

example anonymous object type usage would look like follows.

As you can see in the above example, anonymous objects also behave in the same manner as normal object type definitions.

Some other important points to note about object type are as follows

  • Say you define an object with only member function signature declarations, then you can’t initialize that type of object, it just acts as an API definition.
  • You cannot define member functions in a different package, you have to define them in the same package as object definition.
  • Member functions defined outside of the object shouldn’t have visibility modifiers specified, visibility will get inherited from the function declaration defined inside the object.
  • If an object has a non-defaultable field, then that must be specified as an object constructor parameter. The ideal situation would be that it’s sufficient if the field gets initialized inside the object constructor, but to detect that, we need data flow analysis, so that will be a future improvement :) for the time being, non-defaultable fields should be present as object constructor parameter.

Please note this blog post is written referring to the 0.980.0 Ballerina release.

--

--