1.1. Basic Bindings

In the previous examples, we've use the Felix standard library. It's time to look at how it works.

The Felix language is somewhat novel in that there are no primitive data types, not even bool. Instead, Felix uses binding definitions to declare abstract primitive data types, and bind them to concrete C++ data type.

Here is an example of a binding.

Start C++ section to tut/examples/tut_bind130.flx[1 /1 ]
     1: include "std";
     2: header """
     3: // This is C++ code!
     4: class gauss;
     5: 
     6: struct gauss {
     7:   int x;
     8:   int y;
     9: 
    10:   gauss() : x(0), y(0) {}
    11:   gauss(int _x, int _y) : x(_x), y(_y) {}
    12: 
    13:   gauss operator +(gauss z) const
    14:   {
    15:     return gauss(x+z.x, y+z.y);
    16:   }
    17:   gauss operator *(gauss z) const
    18:   {
    19:     return gauss (x *z.x - y*z.y, x*z.y + y*z.x);
    20:   }
    21: };
    22: """;
    23: 
    24: // Now the Felix binding
    25: type gauss = "gauss";
    26: proc _set : &gauss * gauss = "*$1 = $2;";
    27: fun add: gauss * gauss -> gauss = "$1 + $2";
    28: fun mul: gauss * gauss -> gauss = "$1 * $2";
    29: fun mkgauss: int * int -> gauss = "gauss($1,$2)";
    30: fun real: gauss -> int = "$1.x";
    31: fun imag: gauss -> int = "$1.y";
    32: 
    33: // Now a pure Felix procedure
    34: proc print(z:gauss) {
    35:   print "(";
    36:   print (real z);
    37:   print ", ";
    38:   print (imag z);
    39:   print ")";
    40: }
    41: 
    42: // And two pure Felix functions
    43: fun sqr(z:gauss):gauss = {
    44:   return z * z;
    45: }
    46: 
    47: fun norm(z:gauss): int = {
    48:   return
    49:     real z * real z + imag z * imag z
    50:   ;
    51: }
    52: 
    53: // Finally, some test code
    54: val z1 = mkgauss(1,2);
    55: val z2 = z1 + z1;
    56: val z3 = sqr z2;
    57: val n = norm z3;
    58: print z1; endl;
    59: print z2; endl;
    60: print z3; endl;
    61: print n; endl;
    62: 
End C++ section to tut/examples/tut_bind130.flx[1]
There are lots of things to note here.

First, the header statement

  header """ .... """;
specifies text to be emitted literally in the generated C++ header file: note the trailing semicolon. In this case, we emit a C++ class defining a basic gaussian integer type. There is also a 'body' keyword to literally emit code in the generated implementation (body) file.

Next, we define the felix type gauss.

  type gauss = "gauss";
The definition specifies the Felix name, gauss, and then the C++ name, which also happens to be gauss.

Then we define the semantics of our new primitive by primitive functions and procedures which allow us to manipulate it. Theses consists of a function or procedure name, a type, and the C++ code to generate when the function or procedure is called.

proc _set : &gauss * gauss = "*$1 = $2;";
fun add: gauss * gauss -> gauss = "$1 + $2";
fun mul: gauss * gauss -> gauss = "$1 * $2";
fun mkgauss: int * int -> gauss = "gauss($1,$2)";
fun real: gauss -> int = "$1.x";
fun imag: gauss -> int = "$1.y";
In the definition string, $1 means the first argument of the argument tuple, and $2 means the second, etc.

If the type of an argument component is prefixed by a &, this means that a pointer is passed.

You should note that there is a predefined correspondence between Felix operators and certain function or procedure names. The ones we have used here are:

  Operator  Name
  =         set
  +         add
  *         mul
  ==        eq

Finally, note carefully that functions bind to C++ expressions, whilst procedures bind to C++ statements. This is why the set procedure binding contains a ; inside the string, as well as after it. You can use a compound statement as well.