About Visula

Visula is a typeless object-oriented programming language.  What makes Visula unusual is that it provides two notations: one textual and one visual.  Visula programs can be edited as diagrams in a visual editor, as well as being edited as regular programs in a text editor.

Visula is primarily a visual programming language, hence its name.  The textual notation is clean and simple, and fairly conventional to other OOPLs, however there are a number of simplifications, and syntactical features that are the result of the graphical notation.  For example, Visula is typeless to cut down on the clutter of declaring variables.

Visula is different in that it has no static or global variables.  Instead, variables are imported explicitly from a parent object, or a module.  There is no such thing as a "global scope", rather every variable is a member of a particular object.  This means there are no complex scoping rules, which is a direct consequence of visual notation, where drawing arrows across the program would be untidy.  It is intended that the scoping rules provide a clearer and more robust program structure, since it declares the dependencies within the program, and removes ambiguity.  In any case, global variables are generally considered to be bad architecture.

Visula is also unusual in not making a distinction between classes and functions.  Visula is untyped, so functions are effectively constructors for objects.  Of course, Visula has types internally, but these are not presented to the user.  This has the advantage of simplifying the task of object creation, and merges the concepts of nested functions, methods and nested classes.  A call to create an object may in fact return an object of a completely different type, which adds a lot of flexibility.  [See "new considered harmful" DDJ]

Some examples

For people who can already program, it will be much easier to learn Visula as a textual language, before learning the visual language.

Here is the "Hello World" program in Visula.

    hw [io]
    {
        io.out.writeln("Hello world!")
    }

A Visula program consists of a main function that is the entry point to the program.  Each function is able to import objects from its parent object via the square brackets, so [io] means that the "io" object is imported from the system since hw is not in fact nested in any other function.  { } delimits the contents of the function.  "io" is a separate module, that is in fact one of the standard library modules, that contains a member called "out".  "out" contains a member called "writeln". "io.out.writeln("Hello world!")" calls the io.out.writeln function with the string "Hello world!".

Here are two "factorial" functions in Visula.

  fac [io]
  {
    io.out.writeln(f1(6))
    io.out.writeln(f2(6))

    private f1(n)
    {
      return = n>1 ? n*f1(n-1) : 1
    }

    private f2(n)
    {
      m = 1
      while n>1
        m = m*n
        n = n-1
      return = m
    }
  }

In detail

  fac [io]

Again, the fac program is a self-contained object, that imports the "io" module.

  io.out.writeln(f1(6))

Call the functiom f1 with argument 6, and write the result to io.out.  

  io.out.writeln(f2(6))

Call the functiom f2 with argument 6, and write the result to io.out.  

  private f1(n)

Declare a function called f1, that is private to fac.  f1 takes one
argument, n.  

      return = n>1 ? n*f1(n-1) : 1

Tests whether n>1, and evaluates either n*f1(n-1) or 1.  The result of the expression is assigned to "return", which is a special variable containing the return value of the function.  

    private f2(n)

Declares f2 to be a private member of fac.  

      m = 1

Assign 1 to m  

    while n>1
        m = m*n
        n = n-1
A while loop, that executes the given block.  Loops (and conditions) are delimited by indentation, though it is possible to have several statements on the same line as well.  So writing

        while n>1 m=m*n n=n-1

is perfectly legitimate.

      return = m

Return the value m from the function.

Object orientation

So far, Visula has been introduced as a procedural language, with procedures being nested inside other functions.  Nested functions can access data in the "parent" object, in the following example:
  foo [io]
  {
    x=1
    inc()
    io.out.writeln(x)

    private inc [x]
    {
      x=x+1
    }
  }

Here, the variable x in inc refers to the variable x in foo, since x is declared as a context variable in square brackets.  Given what we now know about object-orientation, it is not a great leap of the imagination to regard "inc" as a /method/ of foo, and for foo to persist beyond its
invocation.  This idea was first conceived by Dahl and Nygaard in the Simula language.

To return an object, a function simply omits a return value, in which case the value returned from the function is the instance of the function (class) itself.  Using this knowledge, we can create a counter class:

  count [io]
  {
    c = counter(4)
    c.add(3)
    io.out.writeln(c.get())

    private counter(init)
    {
      value=init

      public reset [value]  { value=init }
      public inc [value]    { value=value+1 }
      public add(n) [value] { value=value+n }
      public get [value]    { return=value }
    }
  }
When the function counter() is called, it first sets value to init, the initial value.  The remaining nested functions in counter are all declared as "public" this time, so that they may be called by functions other than counter.  The counter function does not return anything, which means that the instance itself is the return value of the function, thereby setting c to be an instance of counter.  The body of count calls counter.add(), setting the value to 7, which is output at the end of count.  Note that a method can get values from both arguments and context as demonstrated by the add() method.

Inheritance is implemented by assigning an object to the "super" member of a function.

    song(artist, title, length)
    {
       // ...
    }
 
    karaokesong(artist, title, length, lyrics) [song]
    {
       super = song(artist, title, length)
       // ...
    }

Modules

Visula's libraries are organised hierarchically, and each module is itself an object.  Thus we could write a math module that has two public members pi and sin.

  math [extern]
  {
    public pi=3.1415926536
    public sin(x) [extern] { return = extern.sin(x) }
  }

We could then use the math module, as in

  usemath [io,math]
  {
    io.out.writeln(math.sin(math.pi))
  }

Modules are constructed at program startup, in the order that they are used.  There is no confusion about name spaces and name clashes since modules must be explicitly imported, and module names must be stated explicitly.

External interface

External functions are declared in the "extern" module, a special object whose members link to external C functions.  The following code enables Visula to use the "sin" function.  The interpreter dynamically links the relevant modules.
  extern
  {
    dll -> (msvcrt, glibc, m)

    sin(x(double)) -> (return(double))
  }

Moving to visual language

Visula has different notations which are all fundamentally equivalent.  There is the textual form (presented here), a structured text mode, and a fully visual mode.

The structured text mode is a hybrid between the fully textual and the fully visual, and serves as a useful stepping stone to understanding the visual notation.  It also helps to have a fall-back position if the visual is too unwieldy, or if some programmers simply dislike visual notation.

Functions are converted to visual form by

With a little practise, it is not hard to convert a function between textual and visual form.

The function bodies can also be shown in visual language, and this helps bring out the structure of the function.  Expressions are converted to visual form by

Now we see why Visula was so particular about its scoping rules.  You cannot draw a line to an object outside the current function, that would make the program look very untidy. The language mandates that all variable references are local, so that arrows are only drawn within the scope of one function.  Making variable usage explicit throughout the program may also provide readability benefits.

This table shows some examples of converting programs to visual notation.

 
Textual form Semi-visual Fully visual
hw [io]
{
  io.out.Writeln("Hello world!")
}
s [io]
{
    io.out.Writeln(Sum(2,2))

    public Sum(a,b)
    {
        return=a+b
    }
}
t [io]
{
    io.out.Writeln(Value())

    public Value
    {
        return=4
    }
}
t [io]
{
   a=45
   b=2*a+9
   io.out.Writeln(b)
}
m
{
  Complex(re,im) [Sqrt]
  {
    public Re [re]
    {
      return=re
    }

    public Im [im]
    {
      return=im
    }
    Modulus [Sqrt,re,im]
    {
      return=Sqrt(re*re + im*im)
    }
  }
}
f
{
    public rfact(n)
    {
        if n>1
            return=n*rfact(n-1)
        else
            return=1
    }
    public mfact(n)
    {
        m=1
        while n>1
            m=m*n
            n=n-1
        return=m
    }
}
p
{
  public Shape
  {
    public Describe
    {
      return="I am a shape"
    }
  }

  public Square [Shape]
  {
    extends = Shape()

    public Describe
    {
      return="I am a square"
    }
  }
}
hw [io]
{
    writer = *io.out.Writeln
    writer("Hello world!")
}