Alfa programming language

Introduction

Alfa is an experimental programming language.

It combines the classic curly-bracket syntax similar to C and JavaScript with multi-clause functions with parameters passed by pattern matching. The patterns are terms and lists in Prolog style with the tail sublist optionally separated by "|" (the bar notation) also in the style of Prolog.

The bar notation is used not only for lists, but also for other variadic terms, for example function printf( fmt| args ) ... or catch ( @error( line, code | args ) )

Alfa is a hobbystic, spare time project - the idea came from the observation that some features of non-mainstream languages (such as Prolog, Haskell) are so attractive that they should raise more interest in the non-academic community of IT engineers if combined with well-known syntax and look-and-feel. The success of JavaScript is a good example here - the language is maybe one the most popular but it is a functional language in some sense - it has first class functions, lambdas, closures, garbage collector - and all this immersed in the curly-bracket syntax of C.

I wanted to test the real thing - to write some programs in such a language - so I created the first Alfa translator. It produced JavaScript code (JavaScript has almost all the needed features ready) - the online version with some simple examples is still available here: Alfa interpreter in JavaScript .

The second incarnation of my language is targeted to Scheme. It is called "Alfa 3" written in Scheme and produces Scheme code as output. GNU Guile is the implementation of Scheme used, but it should be relatively easy to use another one. The Alfa 3 reference manual and the translator itself is available.

Data

The Alfa data types are divided into:

Types Examples
• scalars (numbers, strings, atom-functors) 1, "Hello", @item
• lists (finite-length sequence of data items)[1, 2, ["a", "b"]]
• structures (composed of a head functor and list of arguments) @item( 1, [1] )
• functions (closures) function(x) {return 1+x;}

The patterns are similar to data constructors. The patterns can contain not only constant data, but also the variable names as placeholders. The bar notation known from Prolog can be used in lists and terms - see the examples.

Sample patterns:

[ 1, X ] list pattern
@t( 1, X ) structure pattern
X single variable name
10 single constant
[ 1 | X ] recursive list pattern
@fun( 1, "x" | Tail ) recursive structure pattern
X( 1 ) term with variable name used as functor
@fun( 1, [X( @fun( A | B ) | y )] ) compound term
@fun( 1, "x" ) constant term

Notes:

Term is used as constructor (the expression @f(X, Y) creates the new object from given data) or "destructor" (in functional programming terminology, as "accessor" or "getter", not as "destroyer") - it decomposes structured data, if used in pattern.

Here is an example which creates two element list and calls function printing the first element:

     function  print1( [ x | _ ] ) {        // list - pattern as destructor
         print( x );
     }

     var x = [ 1, 2 ];                      // list - expression as constructor

     print1( x );

And here is the example of recursive function printing a list:

    /* The clause used for empty list */
    function  prnlist( [] ) {
    }

    /* The clause for non-empty list */
    function  prnlist( [x| Tail ] ) {
        print( x, "\n" );        // print the element (in new line)
        prnlist( Tail );         // print the rest of list 
    }

This is common in Lisp and Prolog; Alfa introduces the style to the imperative world of C/JavaScript and allows you to mix multi-clause functions and pattern matching with archetypic instructions "while", "if", "switch", "return", etc.

Language

Alfa has the familiar curly-brace syntax similar to JavaScript and utilizes list and terms in Prolog manner. To give the reader a more complete idea of the language, here follows classic quick-sort example.

   function qsort( [] ) { return []; }

   function qsort( [H| T ] )
   {
        function  append( [], X ) { return X; }
        function  append( [H| T ], X ) { return [ H | append(T, X) ]; }

        function  select_less( x, [] ) { return []; }
        function  select_less( x, [H| T] ) {
                if ( H < x )
                        return [ H | select_less(x, T ) ];
                else
                        return select_less(x, T);
        }

        function  select_notless( x, [] ) { return []; }
        function  select_notless( x, [H| T] )
        {
                if ( H >= x )
                        return [ H | select_notless(x, T ) ];
                else
                        return select_notless(x, T);
        }

        var LNotLess = select_notless( H, T );
        var LLess = select_less( H, T );

        return  append( qsort(LLess), [H| qsort(LNotLess) ] );
   }

In addition to the elements described above, the pattern matching mechanism can be used for exception catching:

   try {
        some_function( x | args );
   }
   catch ( @error( @arithmetic_error | Args ) ) {
        printf( "Arithmetic error in line %d: %s\n", 
                line, 
                sprint_exception_args( Args ));
        throw;
   }
   catch ( _ ) {
        printf( "Unknown error\n" );
        throw;
   }

We also have the "switch" instruction, acting as would be expected:

   switch ( value ) {
        case 1:
                print( "number 1" );
                break;
        case @f( X ):
                print( "functor @f, argument: " + X );
                break;
        case X:
                print( "other: " + X );
   }

JavaScript

Basic language constructs and mechanisms are taken from JavaScript (function syntax, control instructions - switch, while, if, return, string notation). Types are dynamic and the language is safe. The memory allocation is done with garbage collector, the lexical scoping and first class closures work as it should.

Local variables are declared with the var keyword:

   var  x = @functor( 1, "x" );
   var  y = 1;

The loop variable can be declared in the "for" statement:

   for ( var x=[1,2,4,10]; length(x) > 0; x = tail(x) ) 
        print( head( x ), length( x ) );

Download

The old implementation (in JavaScript) can be found online here: JavaScript Alfa interpreter . The current (more complete) version is rewritten in Scheme. The translator from Alfa to Scheme (written in guile scheme) can be downloaded here: alfa-3.01.tgz. The Alfa language manual is attached to alfa3 tgz. It is also available online. For the polish version of the article and manual - look here.