Visula in a Nutshell

Visula is a visual programming language (VPL).  Instead of using a text editor, Visula programs are diagrams created in a visual editor, shown in Figure 1.

 

Figure 1: The editing environment

The language is based upon object-orientation, and programs are built up of nested objects drawn in boxes.  A simple object is shown in Figure 2 - it has a name (fig2) but no contents.

 

Figure 2: Diagram of an object

The object in Figure 2 is a complete Visula program, which will run fine but it doesn’t do anything.  Objects can be given functionality by putting operations in them.  The program in Figure 3 shows an object with data members (embedded in the side of the object), and operations.

 

Figure 3: A series of simple assignments

The object fig3 contains three operations.  Operations are executed in sequence from left to right.  Written as text, the operations are

  1. x ← 2   

  2. y ← x   

  3. z ← x+y*2

Visula is dynamically typed, so types do not need to be declared beforehand, and variables can be assigned any value you give them.  All values are initially zero.

Visula uses the vertical axis for data.  So to assign something to the variable x, the value is actually written on the horizontal line of x.  The light-grey lines in the background are just there to help the eye line up operations with data.  To copy a value from one data location to another, an arrow moves data from one variable to another.  You can also put numerical expressions into operations, as in Operation 3.

In summary, Visula uses the vertical axis for data and the horizontal axis for time, shown in Figure 4.

Figure 4: Visula’s use of the horizontal and vertical axes

Functions are merely objects that return a result.  Figure 5 shows a function to add two numbers together.

Figure 5: A function to sum two numbers

The inputs to the function, a and b, are on the left of the object, while the outputs are on the right, which makes sense if we remember that time goes from left to right.  The special variable return is used for the result of a function.  It is coloured blue by the editor, but the colour has no meaning.

Note that ALL objects follow this simple structure: inputs on the left, outputs on the right.  There are no exceptions.

We can call functions by passing them arguments.  In Figure 5, the Sum function is called with the pair (2,2), which invokes Sum with a=2, and b=2.  The result, 4, is returned from the function, and it is then assigned to the variable four.

 

Figure 6: Calling a local function

There is nothing preventing the function Sum from having local functions of its own.

The notation for classes is almost identical.  Figure 7 shows a class named CALC, which is created using two inputs a and b, and has two data members, sum and diff.  By convention, we’ll use all-uppercase for names of objects used as classes, all-lowercase for values, and first-letter-capital for names of functions or methods.  A class can be thought of as a function whose values persist beyond the end of the function.

 

 

Figure 7: A class

 

The notation for instantiating a class is the same as that of calling a function.  However, the result of the instantiation is an object, not a single value, and this allows multiple outputs of a function, and also allows the nested functions (now called methods) to be called in the created object.  There is no need to worry about deallocation of objects, objects are automatically garbage-collected when they are no longer needed.

 

Figure 8 shows how the CALC class is instantiated, and how the members of the class are accessed.

 

 

Figure 8: Instantiating a class and accessing its members.

 

The first operation creates a new instance of CALC, and assigns the result to calc.  The body of CALC calculates the sum and diff of the inputs a and b.  The second operation accesses the sum member of calc (value=4), and assigns it to four, and the last operation assigns the diff member of calc (value=0) to zero.

 

The data members sum and diff are public.  This means that they can be overwritten, as shown in Figure 9, which is not always desirable.

 

 

Figure 9: Writing to the members of an object

 

Information hiding and data protection are important in programming languages, so it is possible to make members and methods private as shown in Figure 9.

 

 

Figure 10: A class with private data and methods.

 

The CALC object is still initialized with two inputs a and b, but the body of the initialization routine now computes just one variable, sum.  Furthermore, the variable sum is completely enclosed in the CALC object, indicating that it is private.  The arguments a and b are also private.

 

There are two nested functions (a.k.a. methods) in CALC, which are public.  This means that they can be called, and they return the sum and difference of a and b respectively.  The Sum method simply returns the pre-computed value, while the Diff method calculates the difference each time it is called.  Sum and Diff get their inputs from their parent object, CALC.

 

The program in Figure 11 instantiates the new CALC object can calls the new methods to retrieve the values.

 

 

Figure 11: Invoking methods in a simple object

 

There is nothing stopping the CALC class having further nested classes.  In fact, in Visula it is impossible to have a non-nested class, or a non-nested function, as all classes are nested in some other class, except the root which is instantiated anyway.  Nested functions and classes may alter variables in their parent objects, just like methods in other programming languages.

 

Visula’s module system is very simple.  Each imported module is written as an input to the main object.  By using the math and the io modules, Figure 12 writes the value of cos(π) to the standard output.

 

 

Figure 12: Modules are inputs to the root object

 

Modules are in fact objects that are executed before the main object.

 

By default, operations are executed in sequence.  However it is easy to write conditions and loops.  Controls span the operations they control in the horizontal axis, and are placed vertically on their control variable.

 

Let us consider two versions of the factorial function, shown in Figure 13. 

 

 (a)         (b)

 

Figure 13: Recursive implementation of factorial, (a) using a conditional expression, (b) using conditional operations.

 

In text, these functions are

 

Fac1(n)

{

return ← n>1 ? n*Fac1(n-1) : 1;

}

 

Fac2(n)

{

if(n>1)

return ← n*Fac2(n-1);

else

return ← 1;

}

 

Figure 13(a) shows a function with a single operation, consisting of a conditional expression.  The condition n>1 is evaluated, and if true, the function returns n*Fac1(n-1).  If n>1 is not true, the function returns 1.

 

Figure 13(b) shows a similar function, this time with three operations.  The first operation evaluates n>1, which is used as the condition of the loop.  The second operation returns n*Fac2(n-1) if the condition is true, while the third operation returns 1 if the condition is false.  Operation covered by red boxes are executed only when the condition is true, while operations covered by blue boxes are executed only when the condition is false.

 

Figure 14 shows the factorial function written using a loop.

 

 

Figure 14: A looping (iterative) implementation of factorial.

 

In text, this function would be

 

Fac3(n)

{

                m ← 1;

                while(n>1)

                {

                                m ← m*n;

                                n ← n-1;

                }

                return ← m;

}

 

The green box is a while-loop that spans the middle three operations, and is conditional on the expression n>1.  The function loops, decrementing n each time until n>1 is false, and then the value of m is returned.  Controls may be nested.

 

Inheritance is the ability to base a class upon an existing class, to use, specialise or extend its functionality.  Visula supports single- and multiple-inheritance.  The notation for inheritance is shown in Figure 15, which also shows how values in the derived class are used to initialise the base class.

 

 

Figure 15: A class hierarchy of shapes.

 

Each object contains a special member called super.  When this is assigned to, it adds the new base to the object.

 

Another thing Visula can do is create pointers to functions and classes.  This allows types to be passed around as first order values, and provides a handy shortcut for call-backs without the need for function+data pointers, or auxiliary classes.

 

Figure 16 shows an example of a typical “visitor” pattern implemented in Visula.  A vector is created and filled with the values 3, 4 and 5.  The operation in the circle passes a virtual constructor to the for_each method of the vector.  This calls the visit method once for each item in the vector, and the program displays a message and adds the current item n to the sum.  When for_each has finished, visit has totalled the values to 12, which is displayed.

 

Although visit is a local function, it can behave as a method of fig16.

 

 

Figure 16: Creating a vector, then using a virtual constructor to sum the items.

 

When run, the program displays

 

            Visited 3

            Visited 4

            Visited 5

            Total=12

 

The end!

Calum Grant