Features

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


Control Structures

if Conditional

Extend if conditional with declaration, similar to for conditional.

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 and y local to if/else 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 only at start, local to switch body
  case 0:
	...
	int j = 0;                                     // disallow unsafe initialization
  case 1:
	{
		int k = 0;                              // allow at different nesting levels
		...
	  case 2:                                    // disallow case in nested statements
	}
  ...
}

choose Statement

Alternative switch statement with default break from a case clause.

choose ( i ) {
  case 1, 2, 3:
	...
	fallthru;                                      // explicit fall through
  case 5:
	...
	// implicit end of choose (switch break)
  case 7:
	...
	break                            	        // explicit end of choose (redundant)
  default:
	j = 3;
}

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

with Clause/Statement

Open an aggregate scope making its fields directly accessible (like Pascal).

struct S { int i, j; };
int mem( S & this ) with this {           // with clause
	i = 1;                                          // this->i
	j = 2;                                          // this->j
}
int foo() {
	struct S1 { ... } s1;
	struct S2 { ... } s2;
	with s1 {                                    // with statement
		// access fields of s1 without qualification
		with s2 {  // nesting
			// access fields of s1 and s2 without qualification
		}
	}
	with s1, s2 {
		// access unambiguous fields of s1 and s2 without qualification
	}
}

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 ...;
}

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
}

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.

* int p;                                           // int * p;
[5] int a;                                        // int a[5];
* [5] int pa;                                    // int (* pa)[5];
[5] * int ap;                                    // int * ap[5];
* int p1, p2;                                   // int * p1, * p2;
const * const int cpc;                    // const int * const cpc;
const * [ 5 ] const int cpac;           // const int * const cpac[5]
extern [ 5 ] int xa;                         // extern int xa[5]
static * const int sp;                      // static const int * sp;
* [ int ] ( int ) fp;                            // int (* fp)( int )
* [ * [ ] int ] ( int ) gp;                     // int (* (* gp)( int ))[ ];
[5] * [ * [ int ] (int) ] ( int ) hp;         // int (* (* hp[5])( int ))( int );
(* int)x;                                         // (int *)x
sizeof( [ 5 ] * int );                         // sizeof( * int [5] );

References

Rebindable references providing multiple dereferencing. Use when value is accessed more than address.

int x, *p1 = &x, **p2 = &p1, ***p3 = &p2,
	&r1 = x,    &&r2 = r1,   &&&r3 = r2;
***p3 = 3;                                      // change x
r3 = 3;                                          // change x, ***r3
**p3 = ...;                                      // change p1
&r3 = ...;                                       // change r1, (&*)**r3
*p3 = ...;                                       // change p2
&&r3 = ...;                                     // change r2, (&(&*)*)*r3
&&&r3 = p3;                                  // change r3 to p3, (&(&(&*)*)*)r3
int y, z, & ar[3] = { x, y, z };           // initialize array of references
&ar[1] = &z;                                  // change reference array element
typeof( ar[1] ) p;                           // is int, i.e., the type of referenced object
typeof( &ar[1] ) q;                        // is int &, i.e., the type of reference
sizeof( ar[1] ) == sizeof( int );       // is true, i.e., the size of referenced object
sizeof( &ar[1] ) == sizeof( int *);   // is true, i.e., the size of a reference

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. A calls to a constructor/destructor is generated implicitly by the compiler or explicitly by the programmer.

struct S {
	size_t size;
	int * ia;
};
void ?{}( S * s, int asize ) with s {          // constructor operator
	size = asize;                                   // initialize fields
	ia = calloc( size, sizeof( S ) );
}
void ^?{}( S * s ) with s {                       // destructor operator
	free( ia );                                        // de-initialization fields
}
int main() {
	S x = { 10 }, y = { 100 };                  // implict calls: ?{}( x, 10 ), ?{}( y, 100 )
	...                                                    // use x and y
	^(&x){};  ^(&y){};                             // explicit calls to de-initialize
	(&x){ 20 };  (&y){ 200 };                   // explicit calls to reinitialize
	...                                                    // reuse x and y
}                                                            // implict calls: ^?{}( y ), ^?{}( x )

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.

f( 20_hh );                                      // void f( signed char )
f( 21_hhu );                                    // void f( unsigned char )
f( 22_h );                                        // void f( signed short )
f( 23_uh );                                      // void f( unsigned short )
f( 24z );                                          // void f( size_t )

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 };           // implict 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 ); 

Units

Alternative call syntax (literal argument before routine name) to convert basic literals into user literals.

struct Weight { double stones; };

void ?{}( Weight & w ) { w.stones = 0; }                        // operations
void ?{}( Weight & w, double w ) { w.stones = w; }
Weight ?+?( Weight l, Weight r ) { return (Weight){ l.stones + r.stones }; }

Weight ?`st( double w ) { return (Weight){ w }; }         // backquote for units
Weight ?`lb( double w ) { return (Weight){ w / 14.0 }; }
Weight ?`kg( double w ) { return (Weight) { w * 0.1575}; }

int main() {
	Weight w, hw = { 14 };            // 14 stone
	w = 11`st + 1`lb;
	w = 70.3`kg;
	w = 155`lb;
	w = 0x_9b_u`lb;                     // hexadecimal unsigned weight (155)
	w = 0_233`lb;                         // octal weight (155)
	w = 5`st + 8`kg + 25`lb + hw;
}

Overloading

Routines, operators, variables, and literals 0/1 may be overloaded.

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.

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 }

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

Parametric Polymorphism

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

Polymorphic Routine

Routines may have multiple type parameters each with constraints.

forall( otype T | { void ?{}( T *, zero_t ); T ?+?( T, T ); } ) // constraint type, 0 and +
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;
}
S sa[ 5 ];
int i = sum( sa, 5 );                        // use S's 0 and +

Polymorphic Type

Aggregate types 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 * );
};
forall( otype T | sumable( T ) )     // use trait
T sum( T a[ ], size_t size );

Coroutines / Concurrency

Coroutines, threads, and monitors provides advanced control-flow.

Coroutine

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

// Semi-coroutine resume-suspend between main and Fibonacci coroutine.
#include <fstream>
#include <coroutine>

coroutine Fibonacci { int fn; }        // used for communication
void ?{}( Fibonacci & fib ) with fib { fn = 0; }
void main( Fibonacci & fib ) with fib {
	int fn1, fn2;                             // retained between resumes
	fn = 0;  fn1 = fn;                      // 1st case
	suspend();                              // restart last resume
	fn = 1;  fn2 = fn1;  fn1 = fn;     // 2nd case
	suspend();                              // restart last resume
	for ( ;; ) {
		fn = fn1 + fn2;  fn2 = fn1;  fn1 = fn; // general case
		suspend();                        // restart last resume
	}
}
int next( Fibonacci & fib ) with fib {
	resume( fib );                         // restart last suspend
	return fn;
}
int main() {
	Fibonacci f1, f2;
	for ( int i = 1; i <= 10; i += 1 ) {
		sout | next( f1 ) | next( f2 ) | endl;
	}
}
// Full-coroutine resume-resume cycle between coroutines ping and pong.
#include <fstream>
#include <coroutine>

coroutine PingPong {
	const char * name;
	unsigned int N;
	PingPong * part;
};
void ?{}( PingPong & this, const char * name, unsigned int N, PingPong & part ) {
	this.name = name;
	this.N = N;
	this.part = &part;
}
void ?{}( PingPong & this, const char * name, unsigned int N ) {
	this{ name, N, *(PingPong *)0 };
}
void cycle( PingPong & pingpong ) {
	resume( pingpong );
}
void partner( PingPong & this, PingPong & part ) {
	this.part = &part;
	resume( this );
}
void main( PingPong & pingpong ) {						// ping's starter ::main, pong's starter ping
	for ( unsigned int i = 0; i < pingpong.N; i += 1 ) {
		sout | pingpong.name | endl;
		cycle( *pingpong.part );
	} // for
}
int main() {
	enum { N = 20 };
	PingPong ping = { "ping", N }, pong = { "pong", N, ping };
	partner( ping, pong );
}

Thread




		

Monitor



		

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
int a, b, c;
sin | &a | &b | &c;                          // input format depends on variable type
sout | a | b | c | endl;                     // output format depends on constant/variable type
1 2 3                                             // implicit separation between values

GMP

Interface to GMP multi-precise library through type Int

// compute first 40 factorials with complete accuracy
#include <gmp>
sout | "Factorial Numbers" | endl;
Int fact;                                         // multi-precise integer
fact = 1;                                        // 1st case
sout | (int)0 | fact | endl;
for ( unsigned int i = 1; i <= 40; i += 1 ) {
	fact *= i;                                  // general case
	sout | i | fact | endl;
}

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
}