Farango¶
DISCLAIMER: This document is a work in progress. It’s content may change at any given moment and should not be built upon.
Language goals¶
Be distributed out of the box. Concurrency and distribution should be easy to do.
- As a result, the language would benefit from immutable data structures
- Functional languages are best fit candidates for these kind of tasks
Be fast. If the benefits of distributed calculations are outdone by poor optimisation, this would be useless.
Be safe and high-level. The programmer should work on a theoric machine, and as such, the language should abstract away the inner work.
Expressions¶
Everything in Farango has a value – there are no statements, only expressions.
Binary Operators¶
Binary operators are operators taking two parameters. Invoking an operator can be done with two possible syntaxes:
- Operator-like: <expr> <op> <expr>
- Function-like: (<op>)(<expr>, <expr>)
The language shall natively provide the following operators:
Operator Description * Multiplication / Division % Modulo + Addition / Union - Subtraction == Equal != Not equal > Greater than < Less than >= Equal or greater than <= Equal or less than <=> Compare && Logical AND || Logical OR >> Bitwise right shift << Bitwise left shift ^ Bitwise XOR | Bitwise OR & Bitwise AND Unary operators¶
Unary operators, unlike binary operators, only take one parameter.
The language shall natively provide the following operators:
Operator Description ! Logical not ~ Bitwise not - Minus + Plus ++expr Pre-increment --expr Pre-decrement expr++ Post-increment expr-- Post-decrement Operator precedence¶
Operators in an expression have evaluation priority: this is called precedence. An operator takes precedence over an other operator if it is evaluated before the other. As an example, * takes precedence over +, because a + b * c can be expanded to a + (b * c), and not (a + b) * c.
Below is a table of operators sorted from high precedence (top) to low precedence (bottom):
Operator Precedence Postfix expr++ expr-- Unary ++expr --expr +expr -expr ~ ! User-defined Multiplicative * / % Additive + - Shift << >> Relational < > <= >= <=> Equality == != Bitwise AND & Bitwise XOR ^ Bitwise OR | Logical AND && Logical OR || Assignment = += -= *= /= %= &= ^= |= <<= >>= User defined operators¶
User may define or overload operators by declaring a function with the operator symbol enclosed in parenthesis as identifier:
fun (<>)(lhs, rhs) = { lhs != rhs }Here we declare the binary operator <> as an alias of !=. Alternatively, one could implement the operator as:
(<>) = (!=)There are no requirements on the purity of user-defined operators, but programmers should aspire to make their operators pure.
There are also no requirements on operators laws, with some exceptions on default operator overloads:
- +, *, ^, |, &, == and != shall be associative and commutative.
- All comparison operators shall be transitive.
Callables¶
A callable is an object that, given a set of inputs known as parameters produce one output known as the return value through a sequence of computations.
Declaration¶
A callable can be declared with the following syntax:
fun <identifier>(<param0>, <param1>, [...], <paramN>) = <expression>where <identifier> is the name which the callable shall be refered to in the module, <param0> through <paramN> are the identifiers of each parameter, and <expression> is the expression that shall be evaluated when the callable is invoked.
Invokation¶
Callable invokation is done by specifying its identifier, then the list of its parameters enclosed in parenthesis:
<identifier>(<param0>, <param1>, [...], <paramN>)Purity¶
A callable is called pure when it has no side-effects, and when given a set of parameters, two invokations does produce the same return value.
A common example of pure callables are the arithmetic operators.
Functions¶
Functions are the default (and most used) kind of callables. It has an internal context that is the parameters and local variables, and is destroyed when a return value is produced.
Coroutines¶
Coroutines are the second kind of callables, and can be inferred from the usage of the yield control flow keyword. They are much like functions, except that the internal context of the function is not discarded when a return value is produced. Instead, the context is saved, and the coroutine shall resume when it is invoked again later.
Tasks¶
Tasks are the final kind of callables, and they express a way to make asynchronous tasks units that can be passed on different execution environments. Tasks can be inferred from the usage of the offer control flow keyword, and much like functions, their execution context is destroyed after a return value has been offered.
Control Flow¶
In addition to the standard control flow statements, the following statements are provided to change the control flow in any callable:
- return <expr>: returns the value of the given expression in a function. Execution of the current function stops.
- yield <expr>: yields the value of the given expression in a coroutine. Execution resumes after this point when the coroutine is called again.
- offer <expr>: offers the value of the given expression in a task to the underlying task manager. Execution of the current function stops.