Features

Quick tour of C∀ features. What makes C∀ better!


Overloading

Variables, routines, operators, and literals 0/1 may be overloaded.

Variable

Variable names within a block may be overloaded depending on type.

short int MAX = ...;   int MAX = ...;  double MAX = ...;
short int s = MAX;    int i = MAX;    double d = MAX;   // select MAX based on left-hand type

Routine

Routine names within a block may be overloaded depending on the number and type of parameters and returns.

// selection 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)
// selection 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; object-oriented languages require inheritance. (@ ⇒ use C-style initialization.)

#include <time.h>
void ?{}( timespec & t ) {}
void ?{}( timespec & t, time_t sec ) { t.tv_sec = sec; t.tv_nsec = 0; }
void ?{}( timespec & t, time_t sec, time_t nsec ) { t.tv_sec = sec; t.tv_nsec = nsec; }
void ?{}( timespec & t, zero_t ) { t.tv_sec = 0; t.tv_nsec = 0; }
timespec ?+?( timespec & lhs, timespec rhs ) {
	return (timespec)@{ lhs.tv_sec + rhs.tv_sec, lhs.tv_nsec + rhs.tv_nsec };
}
_Bool ?==?( timespec lhs, timespec rhs ) {
	return lhs.tv_sec == rhs.tv_sec && lhs.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;

Parametric Polymorphism

Routines and aggregate type may have multiple type parameters each with constraints.

Trait

Named collection of constraints.

trait sumable( otype T ) {
	void ?{}( T &, zero_t );			// constructor from 0 literal
	T ?+?( T, T );						// assortment of additions
	T ?+=?( T &, T );
	T ++?( T & );
	T ?++( T & );
};

Polymorphic Routine

Routines may have multiple type parameters each with constraints.

forall( otype 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 ( size_t i = 0; i < size; i += 1 )
			total = total + a[i];		// select appropriate +
	return total;
}
int sa[ 5 ];
int i = sum( sa, 5 );					// use int 0 and +

Polymorphic Type

Aggregate types may have multiple type parameters each with constraints.

forall( otype T | sumable( T ) )		// polymorphic, use trait
struct Foo {
	T * x, * y;
};
Foo( int ) foo;
int i = sum( foo.x, 5 );

Declarations

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
}

Alternative Declaration Syntax

Left-to-right declaration syntax, except bit fields.

C∀C
* int p;
[5] int a;
* [5] int pa;
[5] * int ap;
* int p1, p2;
const * const int cpc;
const * [ 5 ] const int cpac;
extern [ 5 ] int xa;
static * const int sp;
* [ int ] ( int ) fp;
* [ * [ ] 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] );

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 deferences (&*)**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.

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 = 10;  data = alloc( size );
}
void ^?{}( VLA & vla ) with ( vla ) {	// destructor
	free( data );
}
void ?{}( VLA & vla, int size, char fill = '\0' ) { // initialization
	vla.[ size, data ] = [ size, alloc( size, fill ) ];
}
void ?{}( VLA & vla, VLA other ) {		// copy, shallow
	vla = other;
}
{
	VLA  x,		y = { 20, 0x01 },	z = y;	// z points to y
	//   x{};	y{ 20, 0x01 };		z{ z, y }; 
	^x{};								// deallocate x
	x{};								// reallocate x
	z{ 5, 0xff };						// reallocate z, not pointing to y
	^y{};								// deallocate y
	y{ x };								// reallocate y, points to x
	x{};								// reallocate x, not pointing to y
}	//  ^z{};  ^y{};  ^x{};

Nested Routines

Nested routines and call-site inferencing provide a localized form of inheritance.

forall( otype 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.

Qualifier Distribution

To reduce duplication, forall and storage-class qualifiers may be distributed over a group of functions/types,

forall( otype T ) {						// distribution block, add forall qualifier to declarations
	struct stack { stack_node(T) * head; };	// generic type
	inline {							// nested distribution block, add forall/inline to declarations
		void push( stack(T) & s, T value ) ...
		// generic operations
	}
}

Literals

Underscore Separator

Numeric literals allow underscores.

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

Integral Suffixes

New integral suffixes hh (half of half of int) for char, h (half of int) for short, and z for size_t. New length suffixes for 8, 16, 32, 64, and 128 bit integers.

20_hh			// signed char
21_hhu			// unsigned char
22_h			// signed short int
23_uh			// unsigned short int
24z				// size_t
20_L8			// int8_t
21_ul8			// uint8_t
22_l16			// int16_t
23_ul16			// uint16_t
24_l32			// int32_t
25_ul32			// uint32_t
26_l64			// int64_t
27_l64u			// uint64_t
26_L128			// int128
27_L128u		// unsigned int128

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 required
void ?{}( S * s, one_t ) with( s ) { i = j = 1; }	// one_t, no parameter name required
int ?!=?( S * op1, S * op2 ) { return op1->i != op2->i || op1->j != op2->j; }
S ?+?( S op1, S op2 ) { return ?{}( op1->i + op2->i, op1->j + op2->j; }

S s0 = { 0, 1 }, s1 = { 3, 4 };			// implicit call: ?{}( s0, zero_t ), ?{}( s1, one_t )
if ( s0 )								// rewrite: s != 0 ⇒ S temp = { 0 }; ?!=?( s, temp )
		s0 = s0 + 1;					// rewrite: S temp = { 1 }; ?+?( s0, temp );

Postfix Function/Call

Alternative call syntax (postfix: literal argument before routine name) to convert basic literals into user literals, where ?` denotes a postfix-function name and ` denotes a postfix-function call..

postfix functionconstant argument callvariable argument callpostfix routine 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;

Postfix Call Examples


Coroutines / Concurrency

Coroutines, monitors, and threads provides advanced control-flow, similar to μC++.

Coroutine

Stackfull semi and full coroutines allow retaining data and execution state between calls.

#include <fstream>
#include <coroutine>

coroutine RunTotal {					// input numbers and return running total
	int input, total;					// communication
};
void ?{}( RunTotal & rntl ) { rntl.total = 0; }
void update( RunTotal & rntl, int input ) with( rntl ) { // helper
	total += input;						// remember between activations
	suspend();							// inactivate on stack
}
void main( RunTotal & rntl ) with( rntl ) {
	for ( ;; ) {
			update( rntl, input );
	}
}
int add( RunTotal & rntl, int input ) {
	rntl.input = input;					// pass input to coroutine
	resume( rntl );
	return rntl.total;					// return total from coroutine
}
int main() {
	RunTotal rntl;
	for ( int i = 0; i < 10; i += 1 ) {
			sout | i | add( rntl, i ) | endl;
	}
}

Coroutine Examples

Monitor

A monitor type defines data protected with mutual exclusion, and the mutex qualifier acquires mutual exclusion. Bulk acquisition of multiple monitors is supported.

#include <thread>
monitor Bank {							// shared resource
	int money;
};
// acquire mutual exclusion of bank on entry
void deposit( Bank & mutex bank, int deposit ) {
	bank.money += deposit;
}
// acquire mutual exclusion of both banks on entry
void transfer( Bank & mutex mybank, Bank & mutex yourbank, int me2you ) {
	deposit( mybank, -me2you );
	deposit( yourbank, me2you );
}

Monitor Examples

Thread

A thread type, T, instance creates a user-level thread, which starts running in routine void main( T & ), and joins on deallocation.

#include <fstream>
#include <thread>
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 | endl;
}
int main() {
	enum { NumThreads = 5 };
	T t[ NumThreads ];					// create/start threads
	T * tp[ NumThreads ];
	for ( int i = 0; i < NumThreads; i += 1 ) {
			tp[i] = new( i + 1 );		// create/start threads
	}
	for ( int i = 0; i < NumThreads; i += 1 ) {
			delete( tp[i] );			// wait for thread to terminate
	}
}										// wait for threads to terminate

Thread Examples


Control Structures

Extended Conditional

Extend if/while conditional with declaration, 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:
	...
	fallthrough;						// explicit fall through
  case 5:
	...
	// implicit end of choose (switch break)
  case 7:
	...
	break								// explicit end of choose (redundant)
  default:
	j = 3;
}

Non-terminating and Labelled fallthrough

Allow fall through to be non-terminating in case clause or have target label providing common code.

non-terminatorlabelled
choose ( ... ) {
  case 3:
	if ( ... ) {
		... fallthru; // 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 for cases 3/4
	// implicit break
  case 4:


choose ( ... ) {
  case 3:
	choose ( ... ) {
	  case 4:
		for ( ... ) {
			// multi-level transfer
			... fallthru 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.

Labelled continue / break

Extend break/continue with a target label to support static multi-level exit (like Java).

LC: {
	... declarations ...
	LS: switch ( ... ) {
		case 3:
			LIF: if ( ... ) {
				LF: for ( ... ) {
					LW: while ( ... ) {
						... break LC; ...		// terminate compound
						... break LS; ...		// terminate switch
						... break LIF; ...		// terminate if
						... continue LF; ...	// continue loop
						... break LF; ...		// terminate loop
						... continue LW; ...	// continue loop
						... break LW; ...		// terminate loop
					} // while
				} // for
			} else {
				... break LIF; ...				// terminate if
			} // if
	} // switch
} // compound

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
	// recover and continue
} catchResume( E e ; boolean-predicate ) {	// resumption handler
	// repair and return
} finally {
	// always executed
}

with Clause/Statement

Open an aggregate scope making its members directly accessible (like Pascal, but open in parallel).

struct S { int i; int j; double m; } s;	// member 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, qualification
	(double)m;							// unambiguous, cast s.m
}

C∀'s ability to overload variables means members 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.

waitfor Statement

Dynamic selection of calls to mutex type.

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 ) ...; when ( c > 5 ) or 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 ...;
}

Libraries

Stream I/O

Polymorphic stream I/O via sin (input) and sout (output) (like C++ cin/cout) with implicit separation.

#include <fstream>						// 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 | endl;				// output format depends on constant/variable type
x 27 2.3								// implicit separation between values:

GMP

Interface to GMP multi-precise library through type Int, e.g., compute first 40 factorials with complete accuracy.

C∀C
#include <gmp>
int main( void ) {
	sout | "Factorial Numbers" | endl;
	Int fact = 1;						// multi-precise integer
	sout | 0 | fact | endl;
	for ( unsigned int i = 1; i <= 40; i += 1 ) {
			fact *= i;
			sout | i | fact | endl;
	}
}
#include <gmp>
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 );
	}
}

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 '\' (backslash) for integral and floating-point types.

2 \ 8u;									// integral result (shifting), 256
-4 \ 3u;								// integral result (multiplication), -64
4 \ -3;									// floating-point result (multiplication), 0.015625
-4 \ -3;								// floating-point result (multiplication), -0.015625
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>
int main() {
	char c = 'a';
	signed char sc = 'a';
	unsigned char uc = 'a';
	sout | c | sc | uc | endl;			// prints a97 97
}

int128 Type

New basic overloadable type for 128-bit integers.

int main() {
	int128 wi = 1;
	unsigned int128 uwi = 2;
	wi += uwi;
}