LEO – A new programming language

“Programming languages are like religions”. This was a quote from Edwin Walder. He claimed that belief was one of the main reasons for a wide adoption of a programming languages. Naturally, it is only one side of the dice, and other aspects are of course important as well. Nevertheless, it is the belief that a language would be “highly productive”, “efficient” and “having a future” that makes people put the effort to learn them.

The reason I present this work is also based on belief. I personally believe that the language presented in the upcoming pages is highly appealing and practically implementable. It is, if I may say so, a new generation language. A clean and intuitive syntax with high level constructs adapted to today’s concerns. It combines major paradigms, is implicitly parallel, network oriented, has built in data manipulation similar to SQL, interoperability with mainstream languages …and much more.

Since all this is in its infancy and highly experimental, please be indulgent. Moreover, since it is also impossible to make an accurate description of the language in a few lines, I will cheat a bit. The language itself will be presented later in details. For now, let us scratch the surface by looking at some examples of high level features.

Message passing

> self <-- myButton : Clic x y
>     self --> screen : MessageBox "You have clicked on the button!"

Ok, nothing really new here. Simple message passing. However, notice that you don’t have to add listeners and so on, all this is done implicitly. You have a direct model-view-controller where event raisers and listeners are automatically connected. You can send or listen: to anyone, to a set of actors or to a specific one. Thus for example, we could make a control agent acting as follows:

> self <-- {reactor1, reactor2, reactor3} : OverHeat
>     self --> ? : Alert
>     self --> reactorsCooler : EmergencyCoolDown

Moreover, this can apply both to local and remote objects. Indeed there is no conceptual difference between local and remote messages. The only difference is the path travelled by the message. This enables us to treat local and remote messages likely:

> self --> www.myserver.net:1234 : "Hello server!"

the above line sends the string “Hello server!” to the given address on port 1234. Using what protocol you may ask? …That’s up to you, but we will see all that later.

Data manipulation

> numbers = {1, 2, 3, 4, 5, 123, 4444, 456456567776889793234235345}
> evenNumbers = {n | n in numbers and n mod 2 == 0}
> oddNumbers = numbers - evenNumbers

The set oddNumbers is thus the substraction of two sets. All other set operations are supported and as a result it is equally powerful and expressive than SQL. For example:

> richGuysFromBelgium = {e.name |
>     e in employees
>     and p in persons
>     and e.id == p.id
>     and e.salary > 5000
>     and p.country == Belgium
>     }

In fact, you can do even much more than in SQL since it is seamlessly integrated in the language and that the set theory used is more powerful. As usual, more on this later.

Cool functions

> f =
>    function (x)
>        result = x^2 + 1

> twice =
>    function(fun)
>        result = function(x)
>            fun(fun(x))

> ff = twice(f)

> print ff(2)     # prints 26

Functions ca be passed as arguments, returned and handled like any other value. Moreover, there is a strong constraint on these: they must be stateless. This implies useful properties. Functions applied with the same arguments will always return the same result. They do not suffer from nor produce side-effects.

Polyvalent objects

> object Color as in {Red, Green, Blue, Yellow}

> object Rectangle
>     has height as Int
>     has width as Int
>     area = height * width

> object ColorizedRectangle extends Rectangle
>     has color as Color

Implicit parallelism

> for e in employees
>     e.salary := 1.1 * e.salary

In the above example, there is no order in which employees salary is updated, it is done parralelly. To illustrate this better, let us consider the following tuple:

> tuple := (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

Each of its elements can be accessed via its position in the tuple:

> print tuple.3    # prints 30

Now, let us see the behavior of the tuple again:

> for i in {2...10}
>     tuple.(i) := tuple.(i-1)

In classical imperative languages, we would obtain a tuple full of 10. Since it is run in parralel here, we obtain:

> print tuple     # prints (10, 10, 20, 30, 40, 50, 60, 70, 80, 90)

Indeed, each entry in the tuple is now equal to the entry in the previous position.

Lazy evaluation

> a = [1] ++ a

This results in an infinite list of ones. However, since the evaluation is not strict but lazy this is a valid statement and the list will be expanded only when the need arises.

Typing and pattern matching

You can either constrain the type of a variable or leave it unconstrained. As you wish.

> declare a
> declare b as Int

> do
>     a := 1
>     b := 2

>     a := "foo bar"        # ok
>     b := "oops"            # error: "oops" is not of type Int

But also more complex types:

> declare t as in {(Int, Int), (String, String)}

> do
>     t := (3,5)            # ok
>     t := ("foo", "bar")    # ok
>     t := (1, "one")        # error

And you can perform pattern matching on those:

> case t of
>     (Int, Int)
>         ...
>     (123, Int)
>         ...
>     (123, 456)
>         ...
>     ("foo", ?)
>         ...
>     ?
>         ...

You can match values, types or ‘?’ to match anything. If several cases are matching, the most specific one will be chosen.

Actors and actions

The programming language is based on two opposite constructs:
– functions which return values and have no side effects
– actions which return nothing and alterate the environment

For instance:

> resetGame = action (difficulty)
> lives := 3
> score := 0
> maze := buildRandomMaze(difficulty) # here a function is called to create a new maze since it doesn’t need to alter the environment

Behaviors

Behaviors is just another term for aspects. The aim of aspect oriented programming is the separation of concerns. Imagine you would want to log each of your function call. Instead of adding a few lines in each function, the idea is to separate both functionalities. The logging behavior could for instance be expressed as:

> behavior logging
> before function call
> print “Calling function ” ++ fun

Synergies

As you saw, there are 5 important constructs:

functions
actions
objects
agents
behaviors

Each of these can contain inner declarations of any other type. Variables can contain any of these, messages can be functions, functions can return agents, actions can alter behaviors… basically everything is possible. The different constructs can be combined like lego blocks and provide very rich ways to interact together.

Meta-programming

The concept is that the program is a data structure itself which can be used like any other data structure. A program is just a list of statements after all. And each of these statements can be reduced to smaller chunks. Like in Scheme/Lisp this leads to the ability to transform the code by itself to make powerful abstractions, making it possible to define one’s own additional constructs.

Practical aspects

This new languages aims to tackle several practical aspects as well in order to:

– be productive out of the box
– network oriented
– automatic parralellization
– webpages as GUI
– playing well with other programming languages
– scales well, in terms of performances

More about it in an upcoming article…

3 Responses to “LEO – A new programming language”

  1. Winheim Raulsh Says:

    Not bad so far, but conceptually,

    a = [1] + a

    is a logical paradox. Maybe there is a better way of modeling it?

    a[0] = 1
    a[n:n>0] = a[n-1]

    “the n’th item of a, where n > 0, equal the n-1th item of a”

    Or something. Keep up the good work!

  2. sidewords Says:

    Thanks for your input. Feedback is always nice. 🙂
    Concerning the example, I replaced the “+” by “++” to stress that concatenation was meant, not addition. When you read the whole example:

    > a = [1] ++ a

    This can be read as follows: ‘a’ is the result of concatenating a list containing a single ‘1’ to a list ‘a’. Thus:

    “This results in an infinite list of ones. However, since the evaluation is not strict but lazy this is a valid statement and the list will be expanded only when the need arises.”

    I.E. when you will ask the fivth element of ‘a’, concatenation will be performed 5 times in a row, keeping the sixth element (‘a’ again) unexpanded.

    …In fact, after a bit thinking …There is no need to expand at all, it is an infinite list of ones by definition and will result in a cyclic list that can be visited like any other list.

  3. LEO - Main page « Side words Says:

    […] A short apetizer & introduction […]

Leave a reply to sidewords Cancel reply