Syntax Overview

Fly has a rather minimalistic syntax, but without getting obscure or hard to read. For instance, keywords to identify classes, namespaces and function does not exist, because the structure of a class makes it clear what is meant.

A simple example

Below is a couple of tiny Fly classes:


org.test.Calculator
{
    add(a: int, b: int): int
    {
        return a + b;
    }

    muldiv(a: int, b: int, c: int): int
    {
       # Instead of an explicit return, we can set the return value directly:
       muldiv = a * b;
       muldiv = muldiv / c;
    }
}

SomeClass : SomeParent
{
   use std.io;

   # Fields are actually instance variables with get/set functions
   # you can replace later on, e.g if you want to compute the value
   # instead of just returning the field variable.
   someField: int;

   # Constructor
   new()
   {
   }

   # An overloaded constructor
   new(x: int)
   {
       someField = x;
   }

   # A void function
   init()
   {
       # An inner function. Note that we use the function assignment
       # syntax instead of using the return statement, which is useful
       # if the function builds the return value in steps.
       inner(x: int): int
       {          
           inner = x*2;

           if (inner > 10)
           {
               inner = 10;
           }        
       }

       someField = 10;
       z: int;
       y: int = 4;

       # Here we use type inference; no explicit type after ':'
       x:= inner(z) * y + z;

       stdout.print(x);
   }
}
 

There are several points worth noting:

  • The namespace and class is defined at once; "org.test.Calculator" denotes a class Calculator in the org.test namespace. Note that namespaces are optional.
  • Functions can be nested. The nested functions can be called by the outer function, or returned and passed as an argument (forming a closure)
  • Functions can be overloaded. The overloading of SomeClass's new function is an example.
  • A colon denotes type annotation. This can be combined with value assignment; for instance, "a: int = 8;".
  • Every variable has a default value; e.g. 'z' will be zero in the example.
  • Type inference is accomplished by leaving out the typer after the colon, e.g. ":=". The type of the right-hand side expression is used as the type of the left-hand side. Note that := is not an operator; rather the : operator lacks a type and is followed by the assignment operator. Hence ":=" and ": =" means the same thing.
  • Fields are actually an instance variable and two functions; a getter and a setter. You can replace the generated implementation of either if you want to.

Functions, lambda expressions and closures

Fly supports the usual member functions as found in C++, Java and C#, as well as nested functions. In addition, function are values, which means they can be assigned to variables and fields, passed as arguments (to form higher-order functions) and returned from other functions.

To form a closure, the lambda operator \ is used.

Test
{
    outer(num1: int):  (int):int
    {
        inner(num2: int): int
        {
            return num1 * num2;
        }

        return \inner;

        # The above could also have been written:
        # return \(num2: int):int { return num1*num2; }
    }    
}
 

You can also call inner functions directly:

Test
{
    outer (someFloat: float):  int
    {
        triple (num: int): int
        {
            return num * 3;
        }

        return triple(5) + someFloat;
    }    
}

Anonymous classes

You can create anonymous classes with fields in an ad-hoc manner by providing the definition, but without a name.

This can be used to implement multiple return values with a concise syntax:

multiret():{res: int; res2: float; }
{        
    multiret.res = 222;
    multiret.res2 = 222.333f;
}

test()
{
   val := multiret();
   val.res = 2;
   val.res2 = 3f;
}
 

Options: