Skip to content

Overview

Chris Harvey edited this page Jun 9, 2022 · 8 revisions

This page is a quick overview of the Solid language. For details, read the Reference and the Specification.

Subsections containing “(FUTURE)” describe planned features that are not yet implemented.

Comments

% line comment (no line breaks)

%% block
comment, with
line breaks,
no nesting %%

Declarations & Assignments

Variables

let variable_name: TypeExpr = expr;

let unfixed reassignable: TypeExpr = expr;
set reassignable = other_expr;

let `váriåblė nāmė`: TypeExpr = expr;

Type Aliases

type TypeName = TypeExpr;
type `Typė Nāmė` = TypeExpr;

Types

Never

No values are assignable to the never type.

functionFails.(); % never completes evaluation

Void

functionReturnsNothing.(); % completes evaluation, but not an actual value

Atoms (FUTURE)

let a: .SINGLETON = .SINGLETON;

Null

let n: null = null;

Boolean

let unfixed b: bool = true;
set b = false;

Integer

let unfixed i: int = 42;
set i = -42;
set i = 42_000;

let bin:  int = \b10_1010;
let quad: int = \q222;
let oct:  int = \o52;
let hex:  int = \x2a;
let htd:  int = \z16; % hexatridecimal (base 36)

Float

let unfixed f: float = 4.2;
set f = 42.;
set f = 0.42;
set f = 42_000.0;
set f = 4.00_000_2;
set f = 6.28318e2;  % 628.318
set f = 6.28318e-2; % 0.0314159

String

String Literals

let string: str = 'hello world';

let with_line_feed: str = 'hello
world'; % line feed is preserved

let with_line_continutation: str = 'hello\
world'; % line feed is converted to a space

let with_escapes: str = '
	- hello\sworld (space)
	- hello\tworld (tab)
	- hello\nworld (line feed)
	- hello\rworld (carriage return)
	- \'apostrophes\'
	- \% percent signs \%
	- \\ back-slashes \\
	- I \u{2764} (❤) Unicode
';

let with_instring_comments: str = '
	This is a string! % line comment ignored
	This is %% multiline
		comment ignored %% a string!
	Use \% to escape a percent sign.
';

String Templates

let template: str = '''hello world''';

let with_line_feed: str = '''hello
world''';

let no_line_continutation: str = '''hello\
world'''; % preserves backslash and line feed

let no_escapes: str = '''
	- \s \t \n \r do not escape
	- \' \% \\    do not escape
	- \u{2764}    does not escape
''';

let no_comments: str = '''
	% in-string line comments are *not* ignored!
	%% in-string multiline comments
		are *not* ignored! %%
''';

let h: str = 'Hello';
let w: str = 'world';
let unfixed interpolation: str =
	'''{{ h }}, {{ w }}!'''; % 'Hello, world!'
set interpolation =
	'''7 * 3 * 2 is {{ 7 * 3 * 2 }}'''; % '7 * 3 * 2 is 42'
set interpolation =
	'''empty {{}} interpolation'''; % 'empty  interpolation'

let unfixed interp_comment: str = '''
	This is {{ %% multiline comment ignored %% }} string content!
''';
set interp_comment = '''
	This is {{ 'string' % line comment ignored
	}} content!
''';

Object

let unfixed anything: obj = null;
set anything = .FORTY_TWO;
set anything = true;
set anything = 42;
set anything = 4.2e+1;
set anything = 'forty-two';

Unknown

let anything: obj = any_object;
let literally_anything: unknown = anything;

Operators

Type Operators

type Nullish      = T?; % T | null
type Exceptionish = T!; % T | Exception % (FUTURE)
type GeneratorOf  = T*; % Generator.<T> % (FUTURE)
type PromiseOf    = T/; % Promise.<T>   % (FUTURE)

type Intersection       = A & B;
type Union              = A | B;
type DiscriminatedUnion = A ^ B; % (FUTURE)

Unary Operators

let is_falsy: bool = !a; % `true` iff `a` is `null`, `false`, of type `void`, or an exception
let is_empty: bool = ?a; % `true` iff `a` is falsy, a zero, or an empty string/collection

let affirm: int | float = +a; % the value of a numeric value, otherwise no-op
let negate: int | float = -a; % negates a numeric value only

let `await`: unknown = a~~; % (FUTURE)
let next:    unknown = a++; % (FUTURE)

Binary Arithmetic

let addition:       int | float = a + b;
let subtraction:    int | float = a - b;
let multiplication: int | float = a * b;
let division:       int | float = a / b;
let expenontiation: int | float = a ^ b;

3   / 2 == 1;
3.0 / 2 == 1.5;

3.0 / 0; % error

4   ^ -2 == 0;
4.0 ^ -2 == 0.0625;

-3  ^ 2  == 9;
-(3 ^ 2) == -9;

a ^ b ^ c == a ^ (b ^ c);

Binary Comparative

let less_than:     bool = a < b;
let less_or_equal: bool = a <= b;
let not_less:      bool = a !< b;

let greater_than:     bool = a > b;
let greater_or_equal: bool = a >= b;
let not_greater:      bool = a !> b;

let instanceof:    bool = a is b;   % (FUTURE)
let notinstanceof: bool = a isnt b; % (FUTURE)

Binary Equality

let identity:    bool = a === b;
let nonidentity: bool = a !== b;
let equality:    bool = a == b;
let inequality:  bool = a != b;

0   === -0;
0   ==  -0;
0.0 !== -0.0; % -0.0 and 0.0 are not identical
0.0 ==  -0.0;
42  !== 42.0;
42  ==  42.0;
''  === '';   % new strings reuse memory
''  ==  '';

Binary Logical

let and:  obj  = a && b; % short-circuits
let nand: bool = a !& b;
let or:   obj  = a || b; % short-circuits
let nor:  bool = a !| b;

Binary Pipe (FUTURE)

let pipe:   bool = a |> b; % `b.(a)`, but evaluates `a` before `b`
let `then`: bool = a ~> b;

Conditional Expression

let conditional: obj = if a then b else c; % short-circuits

Augmentation for Variable Reassignment (FUTURE)

let x: int = 42;

set x  += expr; % x = x +  (expr)
set x  -= expr; % x = x -  (expr)
set x  *= expr; % x = x *  (expr)
set x  /= expr; % x = x /  (expr)
set x  ^= expr; % x = x ^  (expr)
set x &&= expr; % x = x && (expr) % short-circuits
set x ||= expr; % x = x || (expr) % short-circuits

Data Structures (FUTURE)

Tuples

let tuple: [int, float, str] = [3, 4.0, 'five'];
let a: int   = tuple.0; % 3
let b: float = tuple.1; % 4.0
let c: str   = tuple.2; % 'five'

tuple.0  == tuple.[0];
tuple.1  == tuple.[3 - 2];
tuple.-3 == tuple.[0];
tuple.-2 == tuple.[1];
tuple.3;  % TypeError
tuple.-4; % TypeError

[3, 4.0, 'five'] ==  [3, 4.0, 'five']; % equal
[3, 4.0, 'five'] !== [3, 4.0, 'five']; % but not referentially identical
tuple ==  tuple;
tuple === tuple; % a pointer is always referentially identical to itself

Records

let record: [
	alpha:   int,
	bravo:   float,
	charlie: str,
] = [
	alpha=   3,
	bravo=   4.0,
	charlie= 'five',
];
let a: int   = record.alpha;   % 3
let b: float = record.bravo;   % 4.0
let c: str   = record.charlie; % 'five'
record.charles; % invalid

let delta: int = 16;
let record1: [delta: int] = [delta= 32];
let record2: [delta: int] = [delta= delta]; % outer `delta`
delta   == 16;
record1 == [delta= 32];
record2 == [delta= 16];

let record: [echo: bool] = [
	echo= true,
	echo= false, % override
];
record == [echo= false];
record.count == 1; % size

let override_count: [count: int] = [count= 2];
override_count.count == 2; % not size

Lists

let list: (int | float | str)[] = List.<int | float | str>([3, 4.0, 'five']);
let a: int | float | str = list.0; % 3
let b: int | float | str = list.1; % 4.0
let c: int | float | str = list.2; % 'five'
list.count == 3; % size

Dicts

let dict: [:int | float | str] = Dict.<int | float | str>([
	alpha=   3,
	bravo=   4.0,
	charlie= 'five',
]);
let a: int | float | str = dict.alpha;   % 3
let b: int | float | str = dict.bravo;   % 4.0
let c: int | float | str = dict.charlie; % 'five'
dict.count == 3; % size

Sets

let `set`: (int | float | str){} = Set.<int | float | str>([3, 4.0, 'five']);
`set`.has.(3);    % true
`set`.has.(4.0);  % true
`set`.count == 3; % size

Maps

let map: {int | str -> str | [str] | [i: {str -> str}]} = {
	1     -> 'who',
	'2nd' -> ['what'],
	1 + 2 -> [i= {'don’t' -> 'know'}],
};
map.count == 4; % size

map.get.(1)   == 'who';
map.get.(2).0 == 'what';
map.get.(3).i == {'don’t' -> 'know'};

Punning for Record Properties (FUTURE)

let x: int = 42;

[
	y= 43,
	x$,    % x= x
];

Mutable Types

let tuple: mutable [int, float, str] = [3, 4.0, 'five'];
set tuple.0 = 6;
tuple == [6, 4.0, 'five'];

let record: mutable [
	alpha:   int,
	bravo:   float,
	charlie: str,
] = [
	alpha=   3,
	bravo=   4.0,
	charlie= 'five',
];
set record.alpha = 6;
record == [
	alpha=   6,
	bravo=   4.0,
	charlie= 'five',
];

Optional Entries

let tuple: mutable [int, float, ?:str] = [3, 4.0];
tuple.2;  %: str | void % might cause a runtime error
tuple?.2; %: str | null % no runtime error
set tuple.2 = 'five';

let record: mutable [
	alpha?:  int,
	bravo:   float,
	charlie: str,
] = [
	bravo=   4.0,
	charlie= 'five',
];
set record.alpha = 6;

Constant Collections (FUTURE)

[3,  4.0, 'five'] !== [3,  4.0, 'five']; % regular collections allocate new memory
@[3, 4.0, 'five'] === @[3, 4.0, 'five']; % constant collections reuse memory (like strings)
[3,  4.0, 'five'] ==  @[3, 4.0, 'five']; % have the same items

let contains_computable_expressions: [alpha: float] = @[alpha= @[3].0 + 2];

@[my_var, [3]]; %> SyntaxError % cannot contain variables or regular collections
let tuple: mutable [int, float, str] = @[3, 4.0, 'five']; %> TypeError % cannot be mutable

Blocks (FUTURE)

Conditional Statements

if expression then {
	run_if_true;
} else {
	run_otherwise;
};

if expression then {
	run_if_true_then_exit;
} else if expression2 then {
	run_if_true_then_exit;
} else {
	run_otherwise;
};

if true then {
	dangling_else;
};

unless expression then {
	run_if_false;
} else {
	run_otherwise;
};

unless expression then {
	run_if_false_then_exit;
} else unless expression2 then {
	run_if_false_then_exit;
} else {
	run_otherwise;
};

unless false then {
	dangling_else;
};

While Loops

while expression do {
	repeat_as_long_as_true;
	if loop_should_end then {
		break;
	} else if iteration_should_stop_but_continue_looping then {
		continue;
	} else {};
};

do {
	run_once_then_repeat_as_long_as_true;
} while expression;

until expression do {
	repeat_as_long_as_false;
	while inner_condition do {
		if continue_inner_loop_only then {
			continue 0;
		} else if continue_inner_and_outer_loops then {
			continue 1;
		} else {};
	};
};

do {
	run_once_then_repeat_as_long_as_false;
} until expression;

For Loops

for index from start to end by incr do {
	'run while `index` is >= `start` and < `end`,
	increasing `index` by `incr` each iteration,
	and re-evaluating `end` each time.';
};

for index from start to end do {
	'run while `index` is >= `start` and < `end`,
	incrementing `index` by 1 each iteration,
	and re-evaluating `end` each time.';
};

for index in 1..10 do {
	'run while `index` is >= 1 and < 10,
	incrementing `index` by 1 each iteration.';
};

for item of iterable do {
	'run for each item in the iterable…
	could be a tuple, list, set, or generator.';
};

Functions (FUTURE)

func myFunction(x: int, y: float, z: str): bool {
	return true;
}

let result: bool = myFunction.(42, 4.2, '0.42');
result == true;

let call_named: bool = myFunction.(42, z= '0.42', y= 4.2);
func implicitReturn(x: int, y: float, z: str): bool => true;
let my_lambda: (int, float, str) => bool = (x, y, z) { return true; };
let implicit_return: (int, float, str) => bool = (x, y, z) => true;
my_lambda.(42, 4.2, '0.42');          % valid
my_lambda.(x= 42, y= 4.2, z= '0.42'); % invalid

Named Parameters & Arguments

func named_params(x: int, y: float, z: str): bool { return true; }
named_params.(x= 42, y= 4.2, z= '0.42'); % valid
named_params.(42, 4.2, '0.42');          % also valid

let lambda_named_params: (x: int, y: float, z: str) => bool = (x, y, z) { return true; };
lambda_named_params.(x= 42, y= 4.2, z= '0.42'); % valid
lambda_named_params.(42, 4.2, '0.42');          % also valid

func named_aliases(x as a: int, y as b: float, z as c: str): bool {
	x; y; z; %> ReferenceErrors
	a; b; c; % valid
	return true;
};
named_aliases.(x= 42, y= 4.2, z= '0.42');

let lambda_named_aliases: (x: int, y: float, z: str) => bool = (x as a, y as b, z as c) {
	x; y; z; %> ReferenceErrors
	a; b; c; % valid
	return true;
};
lambda_named_aliases.(x= 42, y= 4.2, z= '0.42');

Punning for Funcion Calls

let x: int = 42;

f.(
	44,
	y= 43,
	x$,    % x= x
);

Optional Parameters

func myFunction(
	required: int,
	optional1: int = default1,
	optional2: int = default2,
	optional3 as opt3: int = default3,
): void {
	required;  % int
	optional1; % int
	optional2; % int
	optional3; %> ReferenceError
	opt3;      % int
}
% typeof myFunction == '(required: int, optional1?: int, optional2?: int, optional3?: int) -> {void}'

myFunction.(a);                                % `myFunction.(a, default1, default2, default3)`
myFunction.(a, b);                             % `myFunction.(a, b, default2, default3)`
myFunction.(a, b, optional2= c);               % `myFunction.(a, b, c, default3)`
myFunction.(a, b, optional3= d);               % `myFunction.(a, b, default2, d)`, but evaluates `d` before `default2`
myFunction.(a, b, optional1= c);               % `myFunction.(a, c, default2, default3)`, but evaluates `b` before `c`, then discards `b`
myFunction.(a, b, optional3= d, optional2= c); % `myFunction.(a, b, c, d)`, but evaluates `d` before `c`

Async Functions & Promises

func async myAsyncFunction(x: int, y: float, z: str): bool {
	let xx: int   | Promise.<int>   = x;
	let yy: float | Promise.<float> = y;
	let zz: str   | Promise.<str>   = z;
	let await_x: int   = x~~;
	let await_y: float = y~~;
	let await_z: str   = z~~;
	return true;
}

let promise: bool/ = myAsyncFunction.(42, 4.2, '0.42'); %: Promise.<bool>
promise != true;
let awaited: bool = promise~~;
awaited == true;

let call_and_await: bool = myAsyncFunction.(42, 4.2, '0.42')~~;
call_and_await == true;

many.function~~.calls.().and.()~~.awaits.may~~.be.().chained.()~~;

Generator Functions

func gen myGenFunction(x: int, y: float, z: str): str {
	let xx: int = x + 2;
	yield '''{{ xx }}''';

	let yy: float = y * 2;
	yield '''{{ yy }}''';

	yield '''{{ z }}{{ z }}''';
}

let generator: str* = myGenFunction.(42, 4.2, 'hello'); %: Generator.<str>
generator.done == false;

let xxx: str? = generator++;
generator.done == false;
xxx == '44';

let yyy: str? = generator++;
generator.done == false;
yyy == '8.4';

let zzz: str? = generator++;
generator.done == false;
zzz == 'hellohello';

let last: str? = generator++;
generator.done == true;
last == null;

many.function++.calls.().and.()++.nexts.may++.be.().chained.()++;
Clone this wiki locally