Sunday, January 2, 2022

Back to the future: the joys of high-level programming languages

 


1. Introduction

High-level programming languages are back. More and more developers and companies are using high-level programming languages, either interpreted languages such as Python or functional languages such as Scala or Erlang (or both, such as Clojure). Whatsapp is well-known for using Erlang which it sees as a major reason for maintaining such a large system with few engineers (cf. the Wired article : “Whatsapp serves 900 million users with 50 engineers”). Twitter is using Scala, a functional object-oriented programming language, for similar reasons and with similar success. Many of these object-oriented functional programming languages are 20 or 30 years old, but they have experienced a regain in popularity. This renewed success is both a testimony to Moore’s law (with faster machines, one can afford to lose a little bit of performance to gain expressiveness and speed of development) and to the digital world’s need for agility. High-level programming languages yield shorter code which is faster to modify. This is a key idea which I have developed in my last book (English edition is coming soon).

This is a topic dear to my heart. Thirty years ago, I worked as a research scientist and developed various object-oriented functional programming languages. The last one, CLAIRE, has been the programming toolbox for my own software development projects for the last two decades. This blog post talks about my decision to refresh CLAIRE with a new release, based on a new target environment (i.e., leveraging the superb Go programming language platform produced by Google).  This short post is organized as follows. Section 2 will briefly recall the benefits of interpreted functional object-oriented programming languages. Section 3 describes why and how I decided to create a new release of CLAIRE. CLAIRE was born in the 90s as an open-source project, with a small user community for a couple of years and was used successfully in a number of enterprise decision-aid software. It then lost its appeal and has been dormant for 20 years. 18 months ago, I decided to switch from C++ to Go as a foundation language for CLAIRE. This blog post is a very personal account of the “resurrection” of the CLAIRE programming language. This post is much more personal than what you may find regularly in either of my blogs. You should probably stop reading if you do not have a keen interest with programming languages. To give you “a one-page summary about CLAIRE”, I reproduce here a slide from a 1998 presentation about CLAIRE. If this does not make sense, now is a right time to stop :)

 


2. The Benefits of High-Level Programming Languages

 

Functional programming has many meanings. Here I refer to the ability to manage nameless functions as first-class citizens of the language and pass them as arguments. The archetype of functional programming language is LISP (direct inspiration for Clojure, but also for CLAIRE, since CLAIRE’s ancestor was as LISP overlay). Functional programming matters, because it supports high-level of abstraction, yielding code which is both more concise and easier to maintain because the intent is more visible. Function composition, or applying a function to a set/list with the famous “map” function, is made much simpler when functions (the famous “lambdas” of LISP) are first-class citizens. As any programmer knows, there is a learning curve (with LISP, Scala, Erlang, …) because programming at a higher abstraction level is not always simpler. However, as soon as the code needs to change, this investment pays handsomely. The structure of the program (how functions are composed) is much more visible, and the number of lines that need to be changed is much smaller. Functional programming languages are ideal scripting languages over a library of lower functions precisely because of the capacity to manage functional expressions. There is no better example than Python which has become the standard for combining functions from a machine learning algorithm library.

 

Interpreted languages are an alternative to compiled languages which inherit the “Print(eval(read()))” loop from LISP. An interpreted language is capable running dynamically any code fragment. The performance of modern computers and modern compilers have made interpreted languages almost irrelevant during the last two decades because one could modify code “on the fly” on their favorite IDE and re-run almost instantaneously. However, when prototyping complex algorithms, the interactive capability of the interpreter loop is unmatched. It allows for a complex inspection of the working environment that surpasses the best IDE. The return to popularity of interpreted language – here also, Python is the foremost example – is the consequence of computer performance (Python programs are slow, but usually most of the heavy work is done in the machine learning library that has been compiled earlier) and the need for agility. As soon as the software development process is a learning process, where many iterations are requited to grow the program structure, interpreted languages offers competitive value. To be more explicit, there are two kinds of required agility, which correspond to two time-horizons. If you follow a specification to produce your code, the added value of interpreted languages is very small (any modern IDE will give you enough flexibility). If, on the other hand, the code needs to be fine-tune through multiple iterations where one needs to explore the program output to understand how to improve the current algorithm, then an interpreter’s loop is a great tool.


There are many others programming patterns that contribute to the “high-level” of abstraction. The title of this blog post talks about “high-level programming language”, which is a shortcut for languages with a high level of abstraction. Other famous programming patterns include “object-oriented programming” (with a lot of debates about its virtues or what it means), “rule-based programming” (think of PROLOG and the GOFAI – good old-fashion AI – tools of the 80s) or “set-based programming”, to name a few. Object-oriented programming is controversial precisely because it has many flavors. The “simple kind” of the 80s (SMALLTALK) is actually a great way to increase the abstraction level, while the “compiled kind” that you find in C++ or Go is much more debatable. What raises the “level of abstraction” is the combination of conciseness and a clear semantics (you can express your ideas with fewer lines, and it is easier to understand what they mean). As expressed in the introduction, high-level languages have become more popular because of the request for agility. High-level abstraction means a code that is easier to change and to maintain. Here we mean the capability to change on a longer time-horizon, which is why the arguments apply to compiled languages (such as Swift or Java) as well as interpreted ones.  High-level of abstraction also means a code that is easier to share and to reuse.

 

3. How I got sidetracked when picking a new programming language

 

Five years ago, I decided to write an iOS application and worked for two years with the Swift language. Swift is not interpreted, but it is an elegant language with high level of abstraction, hence I figured it as time to drop CLAIRE for my future projects and to pick a modern programming language. After completing Knomee, I spent the 2020 summer looking at Go, Java, Python, Rust, Node, Scala as possible alternatives. Somewhere along the way, I decided to stick the CLAIRE and to replace the old C++ compiler with a CLAIRE-to-Go compiler.

The first learning from my experiments is that I did not want to lose the flexibility and speed that comes from combining an interpreted language with set-base programming capabilities.  Here is another slide that is extracted from this CLAIRE presentation, it would require a much longer development to give you a true account of the benefits of set-based programming.

 


Set-based programming under the interpreter makes developing complex algorithms much easier (here again, no surprise if Python is so popular). Moving to Go or Java would have been a logical choice (CLAIRE was based on C++ with its own memory management and garbage collection, and it was not doing a first-class job … nor was Swift actually). However, most of my projects are related to GTES (Game-Theoretical Evolutionary Simulation) and require a lot of tuning and iterations.

The second obvious choice would have been to pick Python to replace CLAIRE. However, the same need for iterative algorithm growing means that I was not ready to accept the performance penalty of Python (or Scala, for that matter). This choice is very subjective, depending on what you are doing, you may find that the x2 performance penalty that you pay for using (compiled) CLAIRE over Java / Go is not acceptable (but I would argue that this is limited to specific low-level system programming) or you may think – on the opposite side – that the x50 penalty that you pay with Python is perfectly fine (precisely, if you use Python to write the glue while relying on lower-level compiled libraries). To make this more concrete, the following table is the result of my experiments, using simple functions to evaluate the various programming languages. Performance is “normalized”: it is the execution time divided by the best time (which varies for each test, since each language has its strengths and weaknesses). I have regrouped the tests into four categories (e.g., “function” is a combination of recursive function and problem solving, “object” means creating / reading / updating 10 million of objects, etc.). The global score is weighted which is highly subjective since it reflects my own experience. But you will get a sense of the overall ranking by looking at the full (preliminary) table.



4. Conclusion


If you are intrigued with CLAIRE, feel free to visit the web site and the GitHib repository. CLAIRE is a “cool language” for many reasons other than was mentioned earlier:

  •  Knowledge representation in CLAIRE is made easier, with many “high-level” features that support entity-relationship modelling.
  •  CLAIRE inherits “old-fashion AI” capabilities: hypothetical reasoning and production rules. This comes from CLAIRE background as a “language for combinatorial optimization algorithms”.
  • CLAIRE is self-described, self-compiled and easily extensible.

To give a last example, CLAIRE was designed so that elaborate algorithms, such as the Hungarian Matching Algorithm, could be written in an elegant and concise manner. The following illustration shows the core of the CLAIRE implementation (complete algorithm takes less than 50 lines).

 

 



CLAIRE4 is the new version of this 28ys-old language. It is an “alpha” release because it will require a few full-sized projects to be developed with CLAIRE 4 to reach “beta” stability level. I will apply CLAIRE 4 to “Global Warming Dynamic Games”, a GTES project which I left aside more than 10 years ago but which seem highly relevant in 2022.

 

 

 


3 comments:

Pierre Haren said...

Hey, Yves, could you post the Sudoku problem solution in Claire? Congrats for reviving this part of your scientific adventures!

Yves Caseau said...

https://github.com/ycaseau/CLAIRE4/blob/master/test/nonreg/sudoku.cl

Yves Caseau said...

Sorry for the very long delay ... there are too many bots trying to push advertising as comments ....

 
Technorati Profile