# Features
**Source**: https://cforall.uwaterloo.ca/features/
**Parent**: https://cforall.uwaterloo.ca/~cforall/
Quick tour of
features. What makes
better!
- [Declarations](#)
[Declarations](#Declarations)
[Alternative Declaration Syntax](#AlternativeDeclarationSyntax)
[Rebindable References](#RebindableReferences)
[Tuple](#Tuple)
[Tuple-Returning Function](#TupleReturningFunction)
[Tuple Member Access](#TupleMemberAccess)
[Constructor / Destructor](#CtorDtor)
[Enumeration](#Enumeration)
[Enum Type](#EnumType)
[Enum Inheritance](#EnumInheritance)
[Nested Functions](#NestedFunctions)
[Postfix Function Call](#PostfixFunction)
[Qualifier Distribution](#QualifierDistribution)
- [Literals](#)
[Literals](#Literals)
[Underscore Separator](#UnderscoreSeparator)
[Integral Suffixes](#IntegralSuffixes)
[Pointer Suffix](#PointerSuffix)
[0 / 1](#01literals)
- [Control Structures](#)
[Control Structures](#ControlStructures)
[if / while Conditional](#IfWhileConditional)
[case Clause](#CaseClause)
[switch Statement](#SwitchStatement)
[choose Statement](#ChooseStatement)
[Non-terminating / Labelled fallthrough](#LabelledFallthrough)
[Loop Control](#LoopControl)
[Labelled break / continue](#LabelledBreakContine)
[Extended else](#ExtendedElse)
[Exception Handling](#ExceptionHandling)
[with Clause / Statement](#WithClauseStatement)
[mutex Statement](#MutexStatement)
[waitfor Statement](#WaitforStatement)
- [Overloading](#)
[Overloading](#Overloading)
[Variable](#VariableOverloading)
[Function](#FunctionOverloading)
[Operator](#OperatorOverloading)
[Extending Types](#ExtendExistingType)
- [Polymorphism](#)
[Polymorphism](#Polymorphism)
[Kinds](#Kinds)
[Trait](#Trait)
[Function](#PolyFunction)
[Type](#PolyType)
[Inheritance](#Inheritance)
[Auto Inferencing](#AutoTypeInferencing)
- [Typed Control Flow](#)
[Typed Control Flow](#TypedControlFlow)
[Generator](#Generator)
[Coroutine](#Coroutine)
[Monitor](#Monitor)
[Thread](#Thread)
- [Libraries](#)
[Libraries](#Libraries)
[Stream I/O](#StreamIO)
[String Stream](#StrStream)
[Pseudo Random Number Generator](#PRNG)
[GMP](#GMP)
- [Miscellaneous](#)
[Miscellaneous](#Miscellaneous)
[Backquote Identifiers](#BackquoteIdentifiers)
[Exponentiation Operator](#ExponentiationOperator)
[Remove Definition Keyword](#RemoveDefKeyword)
[char Types](#CharTypes)
[int128 Type](#int128Type)
[basetypeof Type](#basetypeof)
[Keywords](#Keywords)
---
## Declarations
### Alternative Declaration Syntax
Left-to-right declaration syntax, except bit fields. All declaration symbols have identical meaning,
only the order (left-to-right) changes.
| C∀ | C |
| --- | --- |
| ``` * int p; // pointer to int [5] int a; // array of 5 ints * [5] int pa; // pointer to array of 5 ints [5] * int ap; // array of 5 pointers to int * int p1, p2; // distribute pointer const * const int cpc; const * [5] const int cpac; extern [5] int xa; static * const int sp; * [ int ] ( int ) fp; // pointer to function // returning int and taking int * [ * [ ] int ] ( int ) gp; [5] * [ * [ int ] (int) ] ( int ) hp; (* int)x; sizeof( [5] * int ); ``` | ``` int * p; int a[5]; int (* pa)[5]; int * ap[5]; int * p1, * p2; const int * const cpc; const int * const cpac[5] extern int xa[5] static const int * sp; int (* fp)( int ) int (* (* gp)( int ))[ ]; int (* (* hp[5])( int ))( int ); (int *)x sizeof( * int [5] ); ``` |
### Rebindable References
Multi-level *rebindable* references, as an alternative to pointers, to reduces syntactic noise.
```
int x = 1, y = 2, z = 3;
int * p1 = &x, ** p2 = &p1, *** p3 = &p2, // pointers to x
& r1 = x, && r2 = r1, &&& r3 = r2; // references to x
int * p4 = &z, & r4 = z;
*p1 = 3; **p2 = 3; ***p3 = 3; // change x
r1 = 3; r2 = 3; r3 = 3; // change x: implicit dereference *r1, **r2, ***r3
**p3 = &y; *p3 = &p4; // change p1, p2
// cancel implicit dereferences (&*)**r3, (&(&*)*)*r3, &(&*)r4
&r3 = &y; &&r3 = &&r4; // change r1, r2
```
A reference is a handle to an object, like a pointer, but is automatically dereferenced the specified number
of levels. Referencing (address-of &) a reference variable cancels one of the implicit
dereferences, until there are no more implicit references, after which normal expression behaviour applies.
A C++ non-rebindable reference with null pointer checking is provided by a C∀ constant reference.
```
int i;
int & const cr = i; // must initialized, no null pointer
cr = 3; // change i through reference cr
&cr = &i; // fails, assignment to read-only reference cr
const int ci = 3;
const int & const ccr = ci; // must initialized, no null pointer
ccr = 3; // fails, assignment to read-only variable ci
&ccr = &i; // fails, assignment to read-only reference ccr
int & const & const crcr = cr; // must initialized, no null pointer
crcr = 3; // change i through double reference crcr
&crcr = &i; // fails, assignment to read-only 1st reference crcr
&&crcr = &cr; // fails, assignment of read-only 2nd reference crcr
const int & const & const ccrcr = ccr; // must initialized, no null pointer
ccrcr = 3; // fails, assignment of read-only variable ci
&ccrcr = &ci; // fails, assignment of read-only 1st reference ccrcr
&&ccrcr = &ccr; // fails, assignment of read-only 2nd reference ccrcr
```
### Tuple
Formalized lists of elements, denoted by [ ], with parallel semantics.
```
int i;
double x, y;
int f( int, int, int );
f( 2, x, 3 + i ); // technically ambiguous: argument list or comma expression?
f( [ 2, x, 3 + i ] ); // formalized (tuple) element list
[ i, x, y ] = 3.5; // i = 3.5, x = 3.5, y = 3.5
[ x, y ] = [ y, x ]; // swap values
[ int, double, double ] t; // tuple variable
* [ int, double, double ] pt = &t; // tuple pointer
t = [ i + 5, x / 2.0, y * 3.1 ]; // combine expressions into tuple
[ i, x, y ] = *pt; // expand tuple elements to variables
[i, (x, y)] = 3; // comma expression in tuple
x = t.1; // extract 2nd tuple element (zero origin)
[ y, i ] = [ t.2, t.0 ]; // reorder and drop tuple elements
pt->2 = 5.1; // change 3rd tuple element
```
### Tuple-Returning Function
Functions may return multiple values using tuples.
```
[ int, int ] div( int i, int j ) { // compute quotient, remainder
return [ i / j, i % j ]; // return 2 values
}
int g( int, int ); // 2 parameters
int main() {
int quo, rem;
[ quo, rem ] = div( 3, 2 ); // expand tuple elements to variables
g( div( 5, 3 ) ); // expand tuple elements to parameters
quo = div( 7, 2 ).0; // extract quotient element
}
```
#### [Tuple Examples](https://cforall.uwaterloo.ca/features/tuple.shtml)
### Tuple Member Access
Create a tuple by selecting multiple fields by field name/index to rearrange, drop, or duplicate fields.
The aggregate expression to the left of the . in a member tuple expression is evaluated exactly once.
```
[ double, float ] f( int, double ) {}
struct S { char x; int y; double z; } s;
s.[ x, y, z ] = 1; // select by name: s.x = 1, s.y = 1, s.z = 1
s.[ y, z, x ] = [ 3, 3.2, 'x' ]; // rearrange: s.y = 3, s.z = 3.2, s.x = 'x'
f( s.[ y, z ] ); // drop: f( s.y, s.z )
f( s.[ y, y ] ); // duplicate: f( s.y, s.y )
s.[ 0, 1, 3 ] = 1; // select by name: s.0 = 1, s.1 = 1, s.2 = 1
s.[ 1, 3, 0 ] = [ 3, 3.2, 'x' ]; // rearrange: s.1 = 3, s.3 = 3.2, s.0 = 'x'
f( s.[ 1, 3 ] ); // drop: f( s.y, s.z )
f( s.[ 1, 1 ] ); // duplicate: f( s.y, s.y )
[ int, int, long, double ] x;
void f( double, long ) {}
f( x.[ 0, 3 ] ); // select by index: f( x.0, x.3 )
x.[ 0, 1 ] = x.[ 1, 0 ]; // [ x.0, x.1 ] = [ x.1, x.0 ]
[ long, int, long ] y = x.[ 2, 0, 2 ];
struct A { double i; int j; };
struct B { int * k; short l; };
struct C { int x; A y; B z; } v;
v.[ x, y.[ i, j ], z.k ];
[ int, float, double ] f() {};
[ double, float ] x = f().[ 2, 1 ]; // f() called once
```
### Constructor / Destructor
Implicit initialization and de-initialization (like C++). A constructor/destructor name is denoted
by '?{}' / '^?{}', where '?' denotes the operand and '{' '}'
denote the initialization parameters.
```
struct VLA { int size, * data; }; // variable length array of integers
void ?{}( VLA & vla ) with ( vla ) { // default constructor
size = 0; data = 0p;
}
void ?{}( VLA & vla, int size, char fill = '\0' ) { // initialization
vla.[ size, data ] = [ size, alloc( size, fill ) ];
}
void ?{}( VLA & vla, VLA val ) with( val ) { // copy, deep
vla.[ size, data ] = [ size, alloc( size, data ) ];
}
void ^?{}( VLA & vla ) with ( vla ) { // destructor
free( data );
size = 0; data = 0p;
}
int main() {
VLA x, y = { 20, 0x01 }, z = y; // z copies y
// x{}; y{ 20, 0x01 }; z{ z, y };
x{ 5 }; // allocate x
^x{}; // deallocate x
^z{}; // deallocate z
z{ 5, '\xff' }; // reallocate z
^y{}; // deallocate y
y{ z }; // reallocate y, copies z
x{ 10, '\0' }; // reallocate x
} // ^z{}; ^y{}; ^x{};
```
The alternative to constructor initialization is C *designations*, which are similar to named parameters in Python and Scala, except specific to aggregate initializers.
**Note C∀ designations use a colon separator, rather than C assignment, because the latter syntax conflicts with new language features.**
```
struct A {
int w, x, y, z;
};
A a0 = { .x : 4, .z : 1, .x : 8 }; // A a0 = { 0, 8, 0, 1 };
A a1 = { 1, .y : 7, 6 }; // A a1 = { 1, 0, 7, 6 };
A a2[4] = { [2] : a0, [0] : a1, { .z : 3 } }; // A a2[4] = { a1, { 0, 0, 0, 3 }, a0, { 0, 0, 0, 0 } };
```
Any field *not* explicitly initialized is initialized as if it had static storage duration, implying 0 fill.
Later initializers override earlier initializers, so a field with more than one initializer is only initialized by its last initializer.
For example, in the initialization of a0, x is designated twice, and thus initialized to 8.
A designator specifies the current field for initialization, and any undesignated fields pick up where the last initialization left off.
For example, in the initialization of a1, the initializer of y is 7, and the unnamed initializer 6 initializes the next field, z.
A designator name can be a subscript.
For example, in the initialization of a2, the initializer [2] means the second element, and then the previous rules apply for the remaining initializers.
### Enumeration
An enumeration is a compile-time mechanism to alias names to constants, like typedef is a mechanism to alias names to types.
An enumeration type (shortened to *enum*) is a set of names, each called an *enumerator*, aliased to a compile-time constant value.
```
enum Days { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; // enumeration definition, set of 7 enumerators & constants
Days days = Mon; // enumeration declaration and initialization
```
The set of enumerators is injected into the variable namespace at the definition scope.
Hence, enumerators may be overloaded with variable, enum, and function names.
```
int Foo; // type/variable separate namespaces
enum Foo { Bar };
enum Goo { Bar }; // overload Foo.Bar
double Bar; // overload Foo.Bar, Goo.Bar
```
An anonymous enumeration injects enumerators with specific values into a scope.
```
enum { Prime = 103, BufferSize = 1024 };
```
which is better than using:
| C preprocessor | C constant declarations |
| --- | --- |
| ``` #define Mon 0 ... #define Sun 6 ``` | ``` const int Mon = 0, ..., Sun = 6; ``` |
because the enumeration is succinct, has automatic numbering, can appear in case labels, does not use storage, and is part of the language type-system.
### Enum Type
The enumeration can be any type, and an enumerator's value comes from this type.
Because an enumerator is a constant, it cannot appear in a mutable context, e.g. Mon = Sun is disallowed, and has no address (it is an rvalue).
C∀ provides an automatic conversion from an enumerator to its constant's base-type.
The default enum type is int.
If no values are specified for an integral enum type, the enums are automatically numbered starting at zero by one from left to right.
If a value is specified, numbering continues by one from that value.
If an enumerator value is a *constant* expression, the compiler performs constant-folding to obtain the constant value.
C∀ allows other integral types with associated values.
```
enum() Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND };
enum( char ) Letter { A = 'A', B, C, I = 'I', J, K };
enum( long long int ) BigNum { X = 123_456_789_012_345, Y = 345_012_789_456_123 };
```
The empty type, (), implies the enums are pure symbols without values;
hence, there is no default conversion to int.
Non-integral enum types must be explicitly initialized, e.g., double is not automatically numbered by one.
```
// non-integral numeric
enum( double ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 }
// pointer
enum( char * ) Name { Fred = "Fred", Mary = "Mary", Jane = "Jane" };
int i, j, k;
enum( int * ) ptr { I = &i, J = &j, K = &k };
enum( int & ) ref { I = i, J = j, K = k };
// tuple
enum( [int, int] ) { T = [ 1, 2 ] };
// function
void f() {...} void g() {...}
enum( void (*)() ) funs { F = f, F = g };
// aggregate
struct S { int i, j; };
enum( S ) s { A = { 3, 4 }, B = { 7, 8 } };
// enumeration
enum( Letter ) Greek { Alph = A, Beta = B, /* more enums */ }; // alphabet intersection
```
Enumeration Greek may have more or less enumerators than Letter, but the enumerator values *must* be from Letter.
Therefore, Greek enumerators are a subset of type Letter and are type compatible with enumeration Letter, but Letter enumerators are not type compatible with enumeration Greek.
```
Math m = PI; // allowed
double d = PI; // allowed, conversion to base type
m = E; // allowed
m = Alph; // disallowed
m = 3.141597; // disallowed
d = m; // allowed
d = Alph; // disallowed
Letter l = A; // allowed
Greek g = Alph; // allowed
l = Alph; // allowed, conversion to base type
g = A; // disallowed
```
A constructor *cannot* be used to initialize enumerators because a constructor executes at runtime.
A fallback is explicit C-style initialization using @=.
```
enum( struct vec3 ) Axis { Up @= { 1, 0, 0 }, Left @= { 0, 1, 0 }, Front @= { 0, 0, 1 } }
```
### Enumeration Inheritance
C∀ Plan-9 inheritance may be used with enumerations.
```
enum( char * ) Name2 { inline Name, Jack = "Jack", Jill = "Jill" };
enum /* inferred */ Name3 { inline Name2, Sue = "Sue", Tom = "Tom" };
```
Enumeration Name2 inherits all the enumerators and their values from enumeration Name by containment, and a Name enumeration is a subtype of enumeration Name2.
Note, enumerators must be unique in inheritance but enumerator values may be repeated.
The enum type for the inheriting type must be the same as the inherited type;
hence the enum type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for Name3.
When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
Specifically, the inheritance relationship for Names is:
```
Name ⊂ Name2 ⊂ Name3 ⊂ const char * // enum type of Name
```
For the given function prototypes, the following calls are valid.
| | |
| --- | --- |
| ``` void f( Name ); void g( Name2 ); void h( Name3 ); void j( const char * ); ``` | ``` f( Fred ); g( Fred ); g( Jill ); h( Fred ); h( Jill ); h( Sue ); j( Fred ); j( Jill ); j( Sue ); j( "Will" ); ``` |
Note, the validity of calls is the same for call-by-reference as for call-by-value, and const restrictions are the same as for other types.
### Nested Functions
Nested functions and call-site inferencing provide a localized form of inheritance.
```
forall( T | { int ?<?( T, T ); } ) void qsort( const T * arr, size_t size ) { /* use C qsort */ }
int main() {
int ?<?( double x, double y ) { return x > y; } // locally override behaviour
qsort( vals, 10 ); // descending sort
}
```
The local version of ?<? performs ?>? overriding the built-in ?<? so it is
passed to qsort.
### Postfix Function Call
Alternative call syntax where the argument appears before the function name, where ?` denotes a
postfix-function name and ` denotes a postfix-function call. Common usage is for converting basic
literals into user literals.
| postfix function | constant argument call | variable argument call | postfix function pointer |
| --- | --- | --- | --- |
| ``` int ?`h( int s ); int ?`h( double s ); int ?`m( char c ); int ?`m( const char * s ); int ?`t( int a, int b, int c ); ``` | ``` 0`h; 3.5`h; '1'`m; "123" "456"`m; [1,2,3]`t; ``` | ``` int i = 7; i`h; (i + 3)`h; (i + 3.5)`h; ``` | ``` int (* ?`p)( int i ); ?`p = ?`h; 3`p; i`p; (i + 3)`p; ``` |
To pass *multiple* arguments to a postfix function requires a tuple, e.g., [1, 2, 3]`t,
which forms a single argument that is flattened into the multiple arguments. Similarly, if the argument is
an expression, it must be parenthesized, e.g., (i + 3)`h, or only the last operand of the
expression is the argument, e.g., i + (3`h).
#### [Postfix Call Examples](https://cforall.uwaterloo.ca/features/postfixcall.shtml)
### Qualifier Distribution
To reduce duplication, forall and storage-class qualifiers may be distributed over a group of
functions/types. (See [Polymorphic Type](#PolyType))
```
forall( T ) { // distribution block, add forall qualifier to declarations
struct stack { // generic type
T data[10]; // fixed size stack
int depth;
};
inline { // nested distribution block, add forall/inline to declarations
void push( stack(T) & s, T value ) { s.data[s.depth++] = value; ... }
// other generic stack operations
}
}
stack(int) si; // int stack
push( si, 3 ); // inline call
stack(double) sd; // double stack
push( sd, 3.5 ); // inline call
```
---
## Literals
### Underscore Separator
Numeric literals allow underscores (like Ada/Java-8).
```
2_147_483_648; // decimal constant
56_ul; // decimal unsigned long constant
0_377; // octal constant
0x_ff_ff; // hexadecimal constant
0x_ef3d_aa5c; // hexadecimal constant
3.141_592_654; // floating point constant
10_e_+1_00; // floating point constant
0x_ff_ff_p_3; // hexadecimal floating point
0x_1.ffff_ffff_p_128_l; // hexadecimal floating point long constant
L_"\x_ff_ee"; // wide character constant
```
- A sequence of underscores is disallowed, e.g., 12\_\_34 is invalid.
- Underscores may only appear within a sequence of digits (regardless of the digit radix), i.e., an underscore cannot start or end a sequence of digits, e.g., \_1, 1\_ and \_1\_ are invalid (actually, the 1st and 3rd examples are identifier names).
- A numeric prefix may end with an underscore;
a numeric infix may begin and/or end with an underscore;
a numeric suffix may begin with an underscore.
For example, the octal 0 or hexadecimal 0x prefix may end with an underscore 0\_377 or 0x\_ff;
the exponent infix E may start or end with an underscore 1.0\_E10, 1.0E\_10 or 1.0\_E\_10;
the type suffixes U, L, etc. may start with an underscore 1\_U, 1\_ll or 1.0E10\_f.
### Integral Suffixes
New integral suffixes hh (half of half of int) for char, h (half
of int) for short, n (natural number) for int, and z
for size\_t integer types. New length suffixes for 8, 16, 32, 64, and 128 bit integer literals.
| | | |
| --- | --- | --- |
| ``` 20hh // signed char 21hhu // unsigned char 22_h // signed short int 23_uh // unsigned short int 24n // int 24un // unsigned int 25z // size_t ``` | ``` 20_L8 // int8_t 21_ul8 // uint8_t 22l16 // int16_t 23ul16 // uint16_t 24_l32 // int32_t 25_ul32 // uint32_t 26_l64 // int64_t 27_l64u // uint64_t ``` | ``` 26L128 // int128 27_L128u // unsigned int128 ``` |
### Pointer Suffix
New pointer suffix p for creating a pointer literal.
```
0p // null pointer
1p // alternative null pointer (type specific)
0x4ffffep // hardware mapped register
```
### 0 / 1
Literals 0 and 1 are special in C: conditional ⇒ expr != 0 and ++/--
operators require 1.
```
struct S { int i, j; };
void ?{}( S & s, zero_t ) with( s ) { i = j = 0; } // zero_t, no parameter name allowed
void ?{}( S & s, one_t ) with( s ) { [i, j] = 1; } // one_t, no parameter name allowed
int ?!=?( S op, zero_t ) with( op ) { return i != 0 || j != 0; }
S & ?+=?( S & op, one_t ) with( op ) { i += 1; j += 1; return op; }
int main() {
S s0 = 0, s1 = 1; // call: ?{}( s0, zero_t ), ?{}( s1, one_t )
if ( s1 ) { // rewrite: s1 => s1 != 0 => ?!=?( s1, (S){ 0 } )
s1 += 1; // rewrite: ?+=?( s1, (S){ 1 } )
s1 ++; // predefined polymorphic pre/post ++ call ?+=?( S &, one_t )
++ s1;
}
}
```
---
## Control Structures
C∀ identifies inconsistent, problematic, and missing control structures in C, and extends, modifies, and adds control structures to increase functionality and safety.
### if / while Conditional
Extend if / while conditional with declarations, similar to for conditional. (Does
not make sense for do-while.)
```
if ( int x = f() ) ... // x != 0, x local to if/else statement (like C++)
if ( int x = f(), y = g() ) ... // x != 0 && y != 0, x and y local to if/else statement
if ( int x = f(), y = g(); x < y ) ... // x < y, x and y local to if/else statement
if ( struct S { int i; } x = { f() }; x.i < 4 ) ... // x.i < 4, S and x local to if/else statement
while ( int x = f() ) ... // x != 0, x local to while statement (like C++)
while ( int x = f(), y = g() ) ... // x != 0 && y != 0, x and y local to while statement
while ( int x = f(), y = g(); x < y ) ... // x and y local to while statement
while ( struct S { int i; } x = { f() }; x.i < 4 ) ... // x.i < 4, S and x local to while statement
```
### case Clause
Extend case clause with list and subrange.
```
switch ( i ) {
case 1, 3, 5: ... // list
case 6~9: ... // subrange: 6, 7, 8, 9
case 12~17, 21~26, 32~35: ... // list of subranges
}
```
### switch Statement
Extend switch statement declarations and remove anomalies.
```
switch ( x ) {
int i = 0; // allow declarations only at start, local to switch body
case 0:
...
int j = 0; // disallow, unsafe initialization
case 1:
{
int k = 0; // allow at lower nesting levels
...
case 2: // disallow, case in nested statements (no Duff's device)
}
...
}
```
### choose Statement
Alternative switch statement with default break from a case clause.
```
choose ( i ) {
case 1, 2, 3:
...
// implicit end of choose (switch break)
case 5:
...
fallthrough; // explicit fall through
case 7:
...
break // explicit end of choose (redundant)
default:
j = 3;
}
```
### Non-terminating and Labelled fallthrough
Allow fallthrough to be non-terminating within a case clause or have a target label to common code from multiple case clauses.
| non-terminator | labelled | |
| --- | --- | --- |
| ``` choose ( ... ) { case 3: if ( ... ) { ... fallthrough; // goto case 4 } else { ... } // implicit break case 4: ``` | ``` choose ( ... ) { case 3: ... fallthrough common; case 4: ... fallthrough common; common: // below fallthrough // at case-clause level ... // common code cases 3/4 // implicit break case 4: ``` | ``` choose ( ... ) { case 3: choose ( ... ) { case 4: for ( ... ) { // multi-level transfer ... fallthrough common; } ... } ... common: // below fallthrough // at case-clause level ``` |
The target label must be below the fallthrough and may not be nested in a control structure, and
the target label must be at the same or higher level as the containing case clause and located at
the same level as a case clause; the target label may be case default, but only associated
with the current switch/choose statement.
### Loop Control
Extend for / while / do-while iteration control to facilitate coding speed and safety.
Empty conditional implies comparison value of 1 (true).
```
while ( /*empty*/ ) // while ( true )
for ( /*empty*/ ) // for ( ; true; )
do ... while ( /*empty*/ ) // do ... while ( true )
```
The for control is extended with a range and step. A range is a set of values defined by an
optional low value (default to 0), tilde, and high value, L ~ H, with an optional step ~ S
(default to 1), which means an ascending set of values from L to H in positive steps
of S.
```
0 ~ 5 // { 0, 1, 2, 3, 4, 5 }
-8 ~ -2 ~ 2 // { -8. -6, -4, -2 }
-3 ~ 3 ~ 1 // { -3, -2, -1, 0, 1, 2, 3 }
```
Warning: A range in descending order, e.g., 5 ~ -3 is the null (empty) set, i.e., no
values in the set. Warning: A 0 or negative step is undefined. Note, the order of values
in a set may not be the order the values are presented during looping.
The range character, '~', is decorated on the left and right to control how the set values are
presented in the loop body. The range character can be prefixed with '+' or '-'
indicating the *direction* the range is scanned, i.e., from left to right (ascending) or right to left
(descending). If there is no prefix character, it defaults to '+'.
```
-8 ␣~ -2 // ascending, no prefix
0 +~ 5 // ascending, prefix
-3 -~ 3 // descending
```
For descending iteration, the L and H values are *implicitly* switched, and
the increment/decrement for S is toggled. Warning: reversing the range endpoints for
descending order results in an empty set.
```
for ( i; 10 -~ 1 ) // WRONG descending range!
```
Because C uses zero origin, most loops iterate from 0 to *N - 1*. Hence, when scanning a range
during iteration, the last value is dropped, e.g., 0 ~ 5 is 0,1,2,3,4, an exclusive range,
[L,H). To obtain *all* the values in the range, the range character is
postfixed with '=', e.g., 0 ~= 5 is 0,1,2,3,4,5, an inclusive range,
[L,H].
To access the value of the range iteration in the loop body, a loop index is specified before the range.
```
for ( int i; 0 ~ 10 ~ 2 ) { ... i ... } // loop index available in loop body
```
The index type is optional (like C++ auto), where the type is normally inferred from the low
value L because it initializes the index (the type of H can be different from L).
When L is omitted, the type of the required high value H is used, as both L
and H are the same type in this case.
```
for ( i; 1.5 ~ 5 ) // typeof(1.5) i; 1.5 is low value
for ( i; 5.5 ) // typeof(5.5) i; 5.5 is high value
```
The following examples illustrate common \CFA for-control combinations, with the C counter-part in
the comment.
- H is implicit ascending exclusive range [0,H).
```
for ( 5 ) // for ( typeof(5) i; i < 5; i += 1 )
```
- ~= H is implicit ascending inclusive range [0,H].
```
for ( ~= 5 ) // for ( typeof(5) i; i <= 5; i += 1 )
```
- L ~ H is explicit ascending exclusive range [L,H).
```
for ( 1 ~ 5 ) // for ( typeof(1) i = 1; i < 5; i += 1 )
```
- L ~= H is explicit ascending inclusive range [L,H].
```
for ( 1 ~= 5 ) // for ( typeof(1) i = 1; i <= 5; i += 1 )
```
- L -~ H is explicit descending exclusive range (H,L], where L and H are implicitly interchanged to make the range descending.
```
for ( 1 -~ 5 ) // for ( typeof(1) i = 5; i > 0; i -= 1 )
```
- L -~= H is explicit descending inclusive range [H,L], where L and H are implicitly interchanged to make the range descending.
```
for ( 1 -~= 5 ) // for ( typeof(1) i = 5; i >= 0; i -= 1 )
```
There are situations when the for-control actions need to be moved into the loop body, e.g., a
mid-loop exit does not need an iteration-completion test in the for control. The character
'@' indicates that a specific for-control action is ignored, i.e., generates no code.
```
for ( i; @ -~ 10 ) // for ( typeof(10) i = 10; /*empty*/; i -= 1 )
for ( i; 1 ~ @ ~ 2 ) // for ( typeof(1) i = 1; /* empty */; i += 2 )
for ( i; 1 ~ 10 ~ @ ) // for ( typeof(1) i = 1; i < 10; /* empty */ )
for ( i; 1 ~ @ ~ @ ) // for ( typeof(1) i = 1; /* empty */; /* empty */ )
```
Warning: L *cannot* be elided for the ascending range, @ ~ 5,
nor H for the descending range, 1 -~ @, as the loop index is uninitialized.
Warning: H *cannot* be elided in an anonymous loop index, 1 ~ @, as there
is no index to stop the loop.
There are situations when multiple loop indexes are required. The character ':' means add another
index, where any number of indices may be chained in a single for control.
```
for ( i; 5 : j; 2 ~ 12 ~ 3 ) // for ( typeof(i) i = 1, j = 2; i < 5 && j < 12}; i += 1, j += 3 )
for ( i; 5 : j; 2 ~ @ ~ 3 ) // for ( typeof(i) i = 1, j = 2; i < 5; i += 1, j += 3 )
for ( i; 5 : j; 2.5 ~ @ ~ 3.5 ) // no C equivalent, without hoisting declaration of floating-point j
```
The following show more complex loop-control examples across all the different options.
| | |
| --- | --- |
| ``` while () { sout | "empty"; break; } do { sout | "empty"; break; } while (); for () { sout | "empty"; break; } sout | nl | nlOff; for ( 0 ) { sout | "A"; } sout | "zero"; sout | nl; for ( 1 ) { sout | "A"; } sout | nl; for ( 10 ) { sout | "A"; } sout | nl; for ( ~= 10 ) { sout | "A"; } sout | nl; for ( 1 ~= 10 ~ 2 ) { sout | "B"; } sout | nl; for ( 1 -~= 10 ~ 2 ) { sout | "C"; } sout | nl; for ( 0.5 ~ 5.5 ) { sout | "D"; } sout | nl; for ( 0.5 -~ 5.5 ) { sout | "E"; } sout | nl | nl; for ( i; 10 ) { sout | i; } sout | nl; for ( i; ~= 10 ) { sout | i; } sout | nl; for ( i; 1 ~= 10 ~ 2 ) { sout | i; } sout | nl; for ( i; 1 -~= 10 ~ 2 ) { sout | i; } sout | nl; for ( i; 0.5 ~ 5.5 ) { sout | i; } sout | nl; for ( i; 0.5 -~ 5.5 ) { sout | i; } sout | nl; for ( ui; 2u ~= 10u ~ 2u ) { sout | ui; } sout | nl; for ( ui; 2u -~= 10u ~ 2u ) { sout | ui; } sout | nl | nl | nl; enum { N = 10 }; for ( N ) { sout | "N"; } sout | nl; for ( i; N ) { sout | i; } sout | nl; for ( i; -~ N ) { sout | i; } sout | nl | nl | nl; const int low = 3, high = 10, inc = 2; for ( i; low ~ high ~ inc + 1 ) { sout | i; } sout | nl | nl; for ( i; 1 ~ @ ) { if ( i > 10 ) break; sout | i; } sout | nl; for ( i; 2.1 ~ @ ~ @ ) { if ( i > 10.5 ) break; sout | i; i += 1.7; } sout | nl; for ( i; 5 : j; -5 ~ @ ) { sout | i | j; } sout | nl; ``` | ``` empty empty empty zero A A A A A A A A A A A A A A A A A A A A A A B B B B B C C C C C D D D D D E E E E E 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 1 3 5 7 9 10 8 6 4 2 0.5 1.5 2.5 3.5 4.5 5.5 4.5 3.5 2.5 1.5 2 4 6 8 10 10 8 6 4 2 N N N N N N N N N N 0 1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1 3 6 9 1 2 3 4 5 6 7 8 9 10 2.1 3.8 5.5 7.2 8.9 0 -5 1 -4 2 -3 3 -2 4 -1 ``` |
#### [Loop Control Examples](https://cforall.uwaterloo.ca/features/loopctrl.shtml)
### Labelled break / continue
Extend break/continue with a target label to support static multi-level exit (like Java).
```
Compound: {
Try: try {
For: for ( ... ) {
While: while ( ... ) {
Do: do {
If: if ( ... ) {
Switch: switch ( ... ) {
case 3:
break Compound;
break Try;
break For; /* or */ continue For;
break While; /* or */ continue While;
break Do; /* or */ continue Do;
break If;
break Switch;
} // switch
} else {
... break If; ... // terminate if
} // if
} while ( ... ); // do
} // while
} // for
} finally { // always executed
} // try
} // compound
```
### Extended else
The if statement has an optional else clause executed if the conditional is false. This
concept is extended to the while, for, and do looping constructs (like Python).
Hence, if the loop conditional becomes false, looping stops and the corresponding else clause is
executed, if present.
The following example is a linear search for the key 3 in an array, where finding the key is handled with a
break and not finding with the else clause on the loop construct.
| | | |
| --- | --- | --- |
| ``` int a[10]; while ( int i = 0; i < 10 ) { if ( a[i] == 3 ) break; // found i += 1; } else { // not found } ``` | ``` for ( i; 10 ) { if ( a[i] == 3 ) break; // found } else { // not found } ``` | ``` int i = 0; do { if ( a[i] == 3 ) break; // found i += 1; } while( i < 10 ) else { // not found } ``` |
### Exception Handling
Exception handling provides dynamic name look-up and non-local transfer of control.
```
exception_t E {}; // exception type
void f(...) {
... throw E{}; ... // termination
... throwResume E{}; ... // resumption
}
try {
f(...);
} catch( E e ; boolean-predicate ) { // termination handler, optional predicate
// recover and continue
} catchResume( E e ; boolean-predicate ) { // resumption handler, optional predicate
// repair and return
} finally {
// always executed
}
```
### with Clause / Statement
Open an aggregate scope making its fields directly accessible (like Pascal, but open in parallel).
```
struct S { int i; int j; double m; } s; // field i has same type in structure types S and T
struct T { int i; int k; int m; } t;
with ( s, t ) { // open structure variables s and t in parallel
j + k; // unambiguous, s.j + t.k
m = 5.0; // unambiguous, s.m = 5.0
m = 1; // unambiguous, t.m = 1
int a = m; // unambiguous, a = t.m
double b = m; // unambiguous, b = s.m
int c = s.i + t.i; // unambiguous with qualification
(double)m; // unambiguous with cast s.m
}
```
C∀'s ability to overload variables means fields with the same name but
different types are automatically disambiguated, eliminating most qualification when opening multiple
aggregates. Qualification or a cast is used to disambiguate.
### mutex Statement
Local acquisition of monitor mutual exclusion to reduce refactoring and naming (like Java).
| | |
| --- | --- |
| ``` monitor ReadersWriter { ... }; void startRead( ReadersWriter & mutex rw ) { // critical section } void endRead( ReadersWriter & mutex rw ) { // critical section } void read( ReadersWriter & rw ) { // no mutex startRead( rw ); // multiple readers endRead( rw ); } ``` | ``` void read( ReadersWriter & rw ) { // no mutex mutex( rw ) { // start read // critical section } // multiple readers mutex( rw ) { // end read // critical section } } ``` |
As for [monitors functions](#Monitor), a list of monitor objects is allowed and acquired without deadlock.
The special form of the mutex statement for an expression statement removes error-prone duplication by distributing the mutex property across the left-hand side of an assignment expression.
```
mutex( sout ) sout | ...; // serialize printing
mutex( a, b, c ) [a, b, c] = ... // acquire multiple kinds of locking objects without deadlock
```
### waitfor Statement
Dynamic selection of calls to mutex type is controlled by the waitfor statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the function calls that can next acquire mutual exclusion.
```
void main() {
waitfor( r1 : c ) ...;
waitfor( r1 : c ) ...; or waitfor( r2 : c ) ...;
waitfor( r2 : c ) { ... } or timeout( 1 ) ...;
waitfor( r3 : c1, c2 ) ...; or else ...;
when( a > b ) waitfor( r4 : c ) ...; or when ( c > d ) timeout( 2 ) ...; or
when ( c > 5 ) else ...;
when( a > b ) waitfor( r5 : c1, c2 ) ...; or waitfor( r6 : c1 ) ...; or else ...;
when( a > b ) waitfor( r7 : c ) ...; or waitfor( r8 : c ) ...; or timeout( 2 ) ...;
when( a > b ) waitfor( r8 : c ) ...; or waitfor( r9 : c1, c2 ) ...; or
when ( c > d ) timeout( 1 ) ...; or else ...;
}
```
Specifically, a thread calling the monitor is unblocked directly from the calling queue based on function names that can fulfill the cooperation required by the signaller.
(The linear search through the calling queue to locate a particular call can be reduced to *O(1)*.)
Hence, the waitfor has the same semantics as signal\_block, where the signallee thread from the calling queue executes before the signaller, which waits on urgent.
Now when a producer/consumer detects a full/empty buffer, the necessary cooperation for continuation is specified by indicating the next function call that can occur.
---
## Overloading
Variables, functions, operators, and literals 0/1 may be overloaded.
### Variable
Variable names within a block may be overloaded depending on type.
| | | |
| --- | --- | --- |
| ``` short int MAX = 32767; int MAX = 2147483647; double MAX = 1.797...357e+308L; ``` | ``` short int s = MAX; // select MAX based on left-hand type int i = MAX; double d = MAX; ``` | |
### Function
Function names within a block may be overloaded depending on the number and type of parameters and returns.
| | | |
| --- | --- | --- |
| ``` // select based on type and number of parameters void f( void ); // (1) void f( char ); // (2) void f( int, double ); // (3) f(); // select (1) f( 'a' ); // select (2) f( 3, 5.2 ); // select (3) ``` | ``` // select based on type and number of returns char f( int ); // (1) double f( int ); // (2) [int, double] f( int ); // (3) char c = f( 3 ); // select (1) double d = f( 4 ); // select (2) [int, double] t = f( 5 ); // select (3) ``` | |
### Operator
Operator names within a block may be overloaded depending on the number and type of parameters and returns.
An operator name is denoted with '?' for the operand and the standard C operator-symbol.
Operators '&&', '||', and '?:' cannot be overloaded because the short-circuit
semantics cannot be preserved.
```
int ++?( int & op ); // unary prefix increment
int ?++( int & op ); // unary postfix increment
int ?+?( int op1, int op2 ); // binary plus
int ?<=?( int op1, int op2 ); // binary less than
int ?=?( int & op1, int op2 ); // binary assignment
int ?+=?( int & op1, int op2 ); // binary plus-assignment
struct S { int i, j; };
S ?+?( S op1, S op2 ) { // add two structures
return (S){ op1.i + op2.i, op1.j + op2.j };
}
S s1 = { 1, 2 }, s2 = { 2, 3 }, s3;
s3 = s1 + s2; // compute sum: s3 == { 2, 5 }
```
### Extending Types
Existing C library-types can be augmented without inheritance. C++ can only partially extend C types as
constructors, destructors, conversions and operators =, [],
(), -> may only appear in a class.
```
#include <time.h>
void ?{}( timespec & t ) {}
void ?{}( timespec & t, time_t sec ) with(t) { tv_sec = sec; tv_nsec = 0; }
void ?{}( timespec & t, time_t sec, time_t nsec ) with(t) { tv_sec = sec; tv_nsec = nsec; }
void ?{}( timespec & t, zero_t ) with(t) { tv_sec = 0; tv_nsec = 0; }
timespec ?+?( timespec & lhs, timespec rhs ) with(lhs) {
return (timespec)@{ tv_sec + rhs.tv_sec, tv_nsec + rhs.tv_nsec }; // @ ⇒ C-style initialization
}
_Bool ?==?( timespec lhs, timespec rhs ) with(lhs) {
return tv_sec == rhs.tv_sec && tv_nsec == rhs.tv_nsec;
}
timespec ?=?( timespec & t, zero_t ) { return t{ 0 }; }
timespec tv0, tv1 = 0, tv2 = { 3 }, tv3 = { 5, 100000 };
tv0 = tv1;
tv1 = tv2 + tv3;
if ( tv2 == tv3 ) ...
tv3 = tv2 = 0;
```
#### [Overloading Examples](https://cforall.uwaterloo.ca/features/overloading.shtml)
---
## Polymorphism
Functions and aggregate type may have multiple type parameters each with constraints. Aggregate types may
have single type inheritance and multiple implementation inheritance.
### Kinds
There are 5 kinds of polymorphism type.
1. *Opaque* is an incomplete type or pointer/reference with an implicit pointer size and implicitly generated reference and dereference operations.
```
forall( T & ) void f( T & p ) { // incomplete type, no data alignment or size
T & t; // no constructor
T & s = { p }; // pointer initialization
&t = &p; // pointer assignment
t = p; // no data assignment
}
```
2. *Sized* is an incomplete type or pointer/reference with an implicit pointer and data size and implicitly generated reference and dereference operations.
```
forall( T * ) void f( T * p ) { // incomplete type, data alignment and size
T * t; // no constructor
T * s = { p }; // pointer initialization
t = p; // pointer assignment
*t = *p; // data assignment
}
forall( T * ) apply( void (*f)( T * ), unsigned size, T data[size] ) { // C style array
for ( unsigned i = 0 ; i < size ; i += 1 ) {
f( &data[index] ); // subscript computation needs sizeof(T)
}
}
```
3. *Object* is a complete type with alignment/size and implicitly generated default/copy constructors, assignment, and destructor (similar to C++).
An object type can be instantiated, initialized, assigned, and de-instantiated.
```
forall( T ) void f( T p ) { // complete type, data alignment and size
T t; // default constructor
T s = { p }; // copy constructor
t = p; // assignment
} // destructor
int i; // complete type
f( i );
struct S { int f; } s; // complete type
f( s );
forall( T ) struct G { T f; }; // generic type
G(int) g; // complete type
f( g );
```
4. *Variadic* is a type that accepts zero or more inferred type arguments (like C++ variadic template).
```
forall( R, S ) struct pair {R r; S s;};
forall( T *, Params ... // parameter pack
| { void ?{}( T &, Params ); } )
T * new( Params p ) {
return & (*(T *) malloc()){ p };
}
pair(int, char) * x = new( 42, '!' ); // argument pack
```
The new function provides the combination of type-safe malloc with a constructor call, making it impossible to forget constructing dynamically allocated objects.
This function provides the type safety of new in C++, without the need to specify the allocated type again, due to return-type inference.
5. *Dimension* is a generic dimension size for a generalized array.
```
forall( T, [N] ) struct array { // => element type, dimension
// use T and [N]
};
array( int, 5 ) arr; // int arr[5]
array( double, 100 ) arr; // double arr[100]
int i = arr[3]; // subscript checking
double d = arr[3];
forall( T, [N] ) void f( array( T, N ) x, array( T, N ) y ) { ... }
forall( T, [N], [M] ) void g( array( T, N ) x, array( T, M ) y ) { ... }
forall( T, S, [N], [M] ) void h( array( T, N ) x, array( S, M ) y ) { ... }
...
f( a, b ); // a,b same type and dimension
g( a, b ); // a,b same type and different dimensions
h( a, b ); // a,b different types and dimensions
// length accessible at runtime in f, g, and h (like Java .length).
int pivot = N / 2;
for ( i; M ) sout | a[i];
```
### Trait
Named collection of constraints, *both functions and variables*.
```
forall( T )
trait sumable {
void ?{}( T &, zero_t ); // constructor from 0 literal
T ?+?( T, T ); // assortment of additions
T ?+=?( T &, T );
T ++?( T & );
T ?++( T & );
};
```
### Function
Functions may have multiple type parameters each with constraints.
```
forall( T | sumable( T ) ) // polymorphic, use trait
T sum( T a[ ], size_t size ) {
T total = 0; // instantiate T from 0 by calling its constructor
for ( i; size ) total += a[i]; // select appropriate +
return total;
}
int sa[ 5 ];
int i = sum( sa, 5 ); // use int 0 and +=
```
#### [Polymorphic Function Examples](https://cforall.uwaterloo.ca/features/polyfunc.shtml)
### Type
Aggregate types may have multiple type parameters each with constraints.
```
forall( T | sumable( T ) ) { // forall distribution
struct TwoArrays { // polymorphic (generic) type
* T x, y; // two C arrays, new declaration syntax
};
void ?{}( TwoArrays( T ) & ta, int size ) with ( ta ) { // constructor
x = alloc( size ); y = alloc( size );
}
void ^?{}( TwoArrays( T ) & ta ) with ( ta ) { // destructor
free( x ); free( y );
}
}
int main() {
enum { size = 5 };
TwoArrays( int ) tai = { size };
TwoArrays( double ) tad = { size };
for ( i; size ) { tai.x[i] = i; tad.y[i] = i; } // initialize
sout | sum( tai.x, size ) | sum( tad.y, size ); // select int and double sum
}
```
#### [Polymorphic Type Examples](https://cforall.uwaterloo.ca/features/polytype.shtml)
### Inheritance
Multiple implementation/nominal inheritance based on Plan-9 C. No down-casting from derived to base type.
```
struct S0 { int i, j; };
struct S1 { int i, j; };
struct S2 {
inline S1; // implementation/nominal inheritance
int i, j;
inline S0; // implementation/nominal inheritance
};
void f1( S0 s1 ) with(s1) { sout | i | j; }
void f2( S1 s0 ) with(s0) { sout | i | j; }
void f3( S2 s2 ) {
sout | s2.i | s2.j;
S0 s0 = s2; // (S0)s2
with(s0) { sout | i | j; }
S1 & s1 = s2; // (S1)s2
with(s1) { sout | i | j; }
}
int main() {
// S1 i, j S0
S2 s2 = { { 1, 2 }, 3, 4, { 5, 6 } };
f1( s2 ); // (S0)s2
f2( s2 ); // (S1)s2
f3( s2 );
}
$ a.out
5 6
1 2
3 4
5 6
1 2
```
### Auto Type-Inferencing
Auto type-inferencing occurs in a declaration where a variable's type is inferred from its initialization expression type.
| | |
| --- | --- |
| ``` auto j = 3.0 * 4; int i; auto k = i; ``` | ``` #define expr 3.0 * i typeof(expr) j = expr; // use type of initialization expression int i; typeof(i) k = i; // use type of primary variable ``` |
The two important capabilities are:
1. not determining or writing long generic types,
2. ensuring secondary variables, related to a primary variable, always have the same type.
In C∀, typedef provides a mechanism to alias long type names with short ones, both globally
and locally, but not eliminate the use of the short name. gcc provides typeof to declare a
secondary variable from a primary variable. C∀ also relies heavily on the specification of the
left-hand side of assignment for type inferencing, so in many cases it is crucial to specify the type of the
left-hand side to select the correct type of the right-hand expression. Only for overloaded functions
*with the same return type* is variable type-inferencing possible.
Finally, auto is NOT the same as typeof. auto presents the programming
problem of tracking down the auto type. For example, given
```
auto j = ...
```
and the need to write a function to compute using j
```
void rtn( ... parm );
rtn( j );
```
A programmer must somehow work backwards to determine the type of j's initialization expression,
reconstructing an arbitrary number of possibly long generic type-name. In this situation, having the type
name, short alias, or a typeof provides a path back to the type name needed to declare the function
parameter.
There is also the conundrum in type inferencing of when to *brand* a type. That is, when is the type
of the variable more important than the type of its initialization expression. For example, if a change is
made in an initialization expression, it can cause cascading type changes and/or errors. At some point, a
variable type needs to remain constant and the expression to be in error when it changes.
Given typedef and typeof in C∀, and the strong need to use the type of the left-hand
side in inferencing, general auto type-inferencing is not supported at this time. Should a significant need
arise, this feature can be revisited.
---
## Typed Control Flow
Generators, coroutines, monitors, and threads provide advanced typed control-flow, similar
to typed control-flow in [μC++](https://plg.uwaterloo.ca/~usystem/pub/uSystem/uC++.pdf).
### Generator
Stackless asymmetric and symmetric generators allow retaining data and execution state between calls.
Unlike asynchronous await, C∀ generators are programmer scheduled.
State is only retained in the generator type but not the generator's main function.
```
generator Fib {
int fn1, fn;
};
void main( Fib & b ) with(b) {
[fn1, fn] = [1, 0];
for () {
suspend;
[fn1, fn] = [fn, fn + fn1];
}
}
int main() {
Fib f1, f2;
for ( 10 ) {
resume( f1 );
resume( f2 );
sout | f1.fn | f2.fn;
}
}
```
### Coroutine
Stackful asymmetric and symmetric coroutines allow retaining data and execution state between calls.
Unlike asynchronous await, C∀ coroutines are programmer scheduled.
State is retained in both the coroutine type and the coroutine's main function.
```
#include <fstream.hfa>
#include <coroutine.hfa>
// match left/right parenthesis: ((())) match, (() mismatch
enum Status { Cont, Match, Mismatch };
coroutine CntParens {
char ch; // used for communication
Status status;
};
void main( CntParens & cpns ) with( cpns ) { // coroutine main
unsigned int cnt = 0;
for ( ; ch == '('; cnt += 1 ) suspend; // count left parenthesis
for ( ; ch == ')' && cnt > 1; cnt -= 1 ) suspend; // count right parenthesis
status = ch == ')' ? Match : Mismatch;
}
void ?{}( CntParens & cpns ) with( cpns ) { status = Cont; }
Status next( CntParens & cpns, char c ) with( cpns ) { // coroutine interface
ch = c;
resume( cpns );
return status;
}
int main() {
CntParens cpns;
char ch;
for () { // read until end of file
sin | ch; // read one character
if ( eof( sin ) ) { sout | "Mismatch"; break; } // eof ?
Status ret = next( cpns, ch ); // push character for checking
if ( ret == Match ) { sout | "Match"; break; }
if ( ret == Mismatch ) { sout | "Mismatch"; break; }
}
}
```
#### [Coroutine Examples](https://cforall.uwaterloo.ca/features/coroutine.shtml)
### Monitor
A monitor type defines data protected with mutual exclusion, and the mutex qualifier
acquires mutual exclusion. Bulk acquisition of multiple monitors is supported.
Bank transfer problem, where two resources must be locked simultaneously.
| C∀ | C++ |
| --- | --- |
| ``` #include <thread.hfa> monitor BankAccount { int balance; } b1 = { 0 }, b2 = { 0 }; void deposit( BankAccount & mutex b, int deposit ) with(b) { balance += deposit; } void transfer( BankAccount & mutex my, BankAccount & mutex your, int me2you ) { deposit( my, -me2you ); // debit deposit( your, me2you ); // credit } thread Person { BankAccount & b1, & b2; }; void main( Person & person ) with(person) { for ( 10_000_000 ) { if ( random() % 3 ) deposit( b1, 3 ); if ( random() % 3 ) transfer( b1, b2, 7 ); } } int main() { Person p1 = { b1, b2 }, p2 = { b2, b1 }; } // wait for threads to complete ``` | ``` #include <thread> #include <mutex> using namespace std; struct BankAccount { recursive_mutex m; // must be recursive int balance = 0; } b1, b2; void deposit( BankAccount & b, int deposit ) { scoped_lock lock( b.m ); b.balance += deposit; } void transfer( BankAccount & my, BankAccount & your, int me2you ) { scoped_lock lock( my.m, your.m ); deposit( my, -me2you ); // debit deposit( your, me2you ); // credit } void person( BankAccount & b1, BankAccount & b2 ) { for ( int i = 0; i < 10'000'000; i += 1 ) { if ( random() % 3 ) deposit( b1, 3 ); if ( random() % 3 ) transfer( b1, b2, 7 ); } } int main() { thread p1( person, ref(b1), ref(b2) ), p2( person, ref(b2), ref(b1) ); p1.join(); p2.join(); } ``` |
#### [Monitor Examples](https://cforall.uwaterloo.ca/features/monitor.shtml)
### Thread
A thread type, T, instance creates a user-level thread, which starts running in function void main( T &
), and joins on deallocation.
```
#include <fstream.hfa>
#include <thread.hfa>
thread T {
int id;
};
void ?{}( T & t ) { t.id = 0; }
void ?{}( T & t, int id ) { t.id = id; }
void main( T & t ) with( t ) { // thread starts here
sout | id;
}
int main() {
enum { NumThreads = 5 };
T t[ NumThreads ]; // create/start threads
T * tp[ NumThreads ];
for ( i; NumThreads ) {
tp[i] = new( i + 1 ); // create/start threads
}
for ( i; NumThreads ) {
delete( tp[i] ); // wait for thread to terminate
}
} // wait for threads to terminate
```
#### [Thread Examples](https://cforall.uwaterloo.ca/features/thread.shtml)
---
## Libraries
### Stream I/O
C∀ provides polymorphic stream I/O via stdin/sin (input), and stdout/sout
and stderr/serr (output) (like C++ cin/cout/cerr) with implicit
separation and newline (like Python).
```
#include <fstream.hfa> // C∀ stream I/O
char c; int i; double d;
sin | c | i | d; // input format depends on variable type: x 27 2.3
sout | c | i | d; // output format depends on constant/variable type
x 27 2.3 // implicit separation between values and auto newline
[int, [ int, int ] ] t1 = [ 1, [ 2, 3 ] ], t2 = [ 4, [ 5, 6 ] ];
sout | t1 | t2; // print tuples
1, 2, 3 4, 5, 6
```
Polymorphic streams exit and abort provide implicit program termination without and with
generating a stack trace and core file. Stream exit implicitly returns EXIT\_FAILURE to the
shell.
```
exit | "x (" | x | ") has negative value."; // terminate and return EXIT_FAILURE to shell
abort | "x (" | x | ") has negative value."; // terminate and generate stack trace and core file
```
Note, C∀ stream variables stdin, stdout, stderr, exit,
and abort overload C variables stdin, stdout, stderr, and functions
exit and abort, respectively.
The following example is idiomatic C∀ command-line processing and copying an input file to an output file.
Note, a stream variable may be copied because it is a reference to an underlying stream data-structures.
**All unusual I/O cases are handled as exceptions, including end-of-file.**
```
#include <fstream.hfa>
int main( int argc, char * argv[] ) {
ifstream in = stdin; // copy default files
ofstream out = stdout;
try {
choose ( argc ) {
case 3, 2:
open( in, argv[1] ); // open input file first as output creates file
if ( argc == 3 ) open( out, argv[2] ); // do not create output unless input opens
case 1: ; // use default files
default:
exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";
} // choose
} catch( open_failure * ex; ex->istream == ∈ ) { // input file errors
exit | "Unable to open input file" | argv[1];
} catch( open_failure * ex; ex->ostream == &out ) { // output file errors
close( in ); // optional
exit | "Unable to open output file" | argv[2];
} // try
out | nlOff; // turn off auto newline
in | nlOn; // turn on reading newline
char ch;
try {
for () { // read/write characters
in | ch;
out | ch;
} // for
} catch( end_of_file * ) { // end-of-file raised
} // try
} // main
```
#### [Stream Examples](https://cforall.uwaterloo.ca/features/stream.shtml)
### String Stream
The stream types ostrstream and istrstream provide all the stream formatting capabilities to/from a C string
rather than a stream file. The only string stream operations different from a file stream are:
- constructors to create a stream that writes to a write buffer (ostrstream) of size, or reads from a read buffer (istrstream) containing a C string terminated with '\0'.
```
void ?{}( ostrstream &, char buf[], size_t size );
void ?{}( istrstream & is, char buf[] );
```
- write (ostrstream only) writes all the buffered characters to the specified stream (stdout default).
```
ostrstream & write( ostrstream & os, FILE * stream = stdout );
```
There is no read for istrstream.
#### [String Stream Examples](https://cforall.uwaterloo.ca/features/strstream.shtml)
### Pseudo Random Number Generator
There is a sequential PRNG type, only accessible by a single thread (not thread-safe), and a set of global
and companion thread PRNG functions, accessible by multiple threads without contention.
- The PRNG type is for sequential programs, like coroutining:
```
struct PRNG { ... }; // opaque type
void ?{}( PRNG & prng ); // random seed
void ?{}( PRNG & prng, uint32_t seed ); // fixed seed
void set_seed( PRNG & prng, uint32_t seed ); // set seed
uint32_t get_seed( PRNG & prng ); // get seed
uint32_t prng( PRNG & prng ); // [0,UINT_MAX]
uint32_t prng( PRNG & prng, uint32_t u ); // [0,u)
uint32_t prng( PRNG & prng, uint32_t l, uint32_t u ); // [l,u]
uint32_t calls( PRNG & prng ); // number of calls
```
Sequential execution is repeatable given the same starting seeds.
The following shows an example that creates two sequential PRNGs, sets both to the same
seed (1009), and illustrates the three forms for generating random values, where both PRNGs
generate the same sequence of values.
```
PRNG prng1, prng2;
set_seed( prng1, 1009 ); set_seed( prng2, 1009 );
for ( 10 ) {
// Do not cascade prng calls because side-effect functions called in arbitrary order.
sout | nlOff | prng( prng1 ); sout | prng( prng1, 5 ); sout | prng( prng1, 0, 5 ) | '\t';
sout | prng( prng2 ); sout | prng( prng2, 5 ); sout | prng( prng2, 0, 5 ) | nlOn;
}
37301721 2 2 37301721 2 2
1681308562 1 3 1681308562 1 3
290112364 3 2 290112364 3 2
1852700364 4 3 1852700364 4 3
733221210 1 3 733221210 1 3
1775396023 2 3 1775396023 2 3
123981445 2 3 123981445 2 3
2062557687 2 0 2062557687 2 0
283934808 1 0 283934808 1 0
672325890 1 3 672325890 1 3
```
- The PRNG global and companion thread functions are for concurrent programming, such as randomizing
execution in short-running programs, e.g., yield( prng() % 5 ).
```
void set_seed( uint32_t seed ); // set global seed
uint32_t get_seed(); // get global seed
// SLOWER
uint32_t prng(); // [0,UINT_MAX]
uint32_t prng( uint32_t u ); // [0,u)
uint32_t prng( uint32_t l, uint32_t u ); // [l,u]
// FASTER
uint32_t prng( thread$ & th ); // [0,UINT_MAX]
uint32_t prng( thread$ & th, uint32_t u ); // [0,u)
uint32_t prng( thread$ & th, uint32_t l, uint32_t u ); // [l,u]
```
The only difference between the two sets of prng routines is performance.
Because concurrent execution is non-deterministic, seeding the concurrent PRNG is less important, as
repeatable execution is impossible. Hence, there is one system-wide PRNG (global seed) but each
C∀ thread has its own non-contended PRNG state. If the global seed is set, threads start with
this seed, until it is reset and than threads start with the reset seed. Hence, these threads generate
the same sequence of random numbers from their specific starting seed. If the global seed
is *not* set, threads start with a random seed, until the global seed is set. Hence, these
threads generate different sequences of random numbers. If each thread needs its own seed, use a
sequential PRNG in each thread.
The following shows an example using the slower/faster concurrent PRNG in the program main and a thread.
```
thread T {};
void main( T & th ) { // thread address
for ( i; 10 ) {
sout | nlOff | prng(); sout | prng( 5 ); sout | prng( 0, 5 ) | '\t'; // SLOWER
sout | nlOff | prng( th ); sout | prng( th, 5 ); sout | prng( th, 0, 5 ) | nlOn; // FASTER
}
}
int main() {
set_seed( 1009 );
thread$ & th = *active_thread(); // program-main thread-address
for ( i; 10 ) {
sout | nlOff | prng(); sout | prng( 5 ); sout | prng( 0, 5 ) | '\t'; // SLOWER
sout | nlOff | prng( th ); sout | prng( th, 5 ); sout | prng( th, 0, 5 ) | nlOn; // FASTER
}
sout | nl;
T t; // run thread
}
37301721 2 2 1681308562 1 3
1681308562 1 3 1852700364 4 3
290112364 3 2 1775396023 2 3
1852700364 4 3 2062557687 2 0
733221210 1 3 672325890 1 3
1775396023 2 3 873424536 3 4
123981445 2 3 866783532 0 1
2062557687 2 0 17310256 2 5
283934808 1 0 492964499 0 0
672325890 1 3 2143013105 3 2
// same output as above from thread t
```
### GMP
Interface to GMP multi-precise library through type Int, e.g., compute first 41 factorials with
complete accuracy.
| C∀ | C |
| --- | --- |
| ``` #include <gmp.hfa> // provide type Int int main( void ) { sout | "Factorial Numbers"; Int fact = 1; // multi-precise integer sout | 0 | fact; // special case for 0! for ( i; 1 ~= 40 ) { fact *= i; sout | i | fact; } } ``` | ``` #include <gmp.h> int main( void ) { gmp_printf( "Factorial Numbers\n" ); mpz_t fact; mpz_init_set_ui( fact, 1 ); gmp_printf( "%d %Zd\n", 0, fact ); for ( unsigned int i = 1; i <= 40; i += 1 ) { mpz_mul_ui( fact, fact, i ); gmp_printf( "%d %Zd\n", i, fact ); } } ``` |
Large constants are created using numerical strings.
```
Int x = { "50000000000000000000" };
x = "12345678901234567890123456789"`mp + "12345678901234567890123456789"`mp;
```
---
## Miscellaneous
### Backquote Identifiers
Keywords as identifier to deal with new keyword clashes in legacy code.
```
int ``int, ``forall; // keywords as identifiers
``forall = ``int = 5;
``int += 7;
```
### Exponentiation Operator
New binary exponentiation operator x \ y (backslash) for integral and floating-point
types. Multiplication is *O(log y)*. Overflow for a large exponent or negative exponent returns zero.
```
1 \ 0; // integral result, 1 convention
1 \ 1; // integral result, 1
2 \ 8; // integral result (shifting), 256
-4 \ 3; // integral result (multiplication), -64
5 \ 3; // integral result (multiplication), 125
5 \ 32; // integral result (multiplication), 0 overflow
5L \ 32; // integral result (multiplication), 3273344365508751233
5L \ 64; // integral result (multiplication), 0 overflow
-4 \ -3; // integral result (multiplication), 0 reciprocal fraction
-4.0 \ -3; // floating-point result (logarithm), -0.015625 reciprocal fraction
4.0 \ 2.1; // floating-point result (logarithm), 18.3791736799526
(1.0f+2.0fi) \ (3.0f+2.0fi); // complex floating-point result (logarithm), 0.264715-1.1922i
```
### Remove Definition Keyword
Keywords struct and enum are not required in a definition (like C++).
```
struct S { ... };
enum E { ... };
S s; // "struct" before S unnecessary
E e; // "enum" before E unnecessary
```
### char Types
char, signed char, and unsigned char are distinct types and may be overloaded.
```
#include <fstream.hfa>
int main() {
char c = 'a';
signed char sc = 'a';
unsigned char uc = 'a';
sout | c | sc | uc; // prints a97 97
}
```
### int128 Type
New basic overloadable type for 128-bit integers.
```
int main() {
int128 wi = 1;
unsigned int128 uwi = 2;
wi += uwi;
}
```
### basetypeof Type
Pseudo function like typeof returning the base type of a type or variable.
```
basetypeof( const unsigned int ) usi; // unsigned int
volatile const signed char ch;
basetypeof( ch ) sc; // signed char
enum { N = 3 };
basetypeof( N ) si; // signed int
```
### Keywords
C∀ introduces the following new keywords, which cannot be used as identifiers without backquotes.
| | | | | | | |
| --- | --- | --- | --- | --- | --- | --- |
| ``` basetypeof choose coroutine disable ``` | ``` enable exception fallthrough ``` | ``` finally fixup forall generator ``` | ``` int128 monitor mutex one_t ``` | ``` report suspend throw throwResume ``` | ``` trait try virtual waitfor ``` | ``` when with zero_t ``` |
C∀ introduces the following new quasi-keywords, which can be used as identifiers.
| | |
| --- | --- |
| ``` catch catchResume finally ``` | ``` fixup or timeout ``` |