-
Notifications
You must be signed in to change notification settings - Fork 0
Overview
Chris Harvey edited this page Jul 10, 2024
·
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.
% line comment (no line breaks)
%% block
comment, with
line breaks,
no nesting %%
let variable_name: TypeExpr = expr;
let unfixed reassignable: TypeExpr = expr;
set reassignable = other_expr;
let 'váriåblė nāmė': TypeExpr = expr;
let optional_variable?: TypeExpr;
optional_variable == null; %== true
set optional_variable = expr;
type TypeName = TypeExpr;
type 'Typė Nāmė' = TypeExpr;
functionThrows.(); % never completes execution; throws an error
let n: never = sync {
throw Exception.("n is never assigned");
};
let a: .key = .key;
let b: .'quoted-key' = .'quoted-key';
let n: null = null;
let var b: bool = true;
set b = false;
let var i: int = 42;
set i = -42;
set i = 42_000;
let bin: int = \b10_1010;
let quad: int = \q222;
let sex: int = \x110;
let oct: int = \o52;
let hex: int = \x2a;
let htd: int = \z16; % hexatridecimal (base 36)
let var 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
let string: str = "hello world";
let with_line_feed: str = "line feed is
preserved";
let with_line_continutation: str = "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.
";
let template: str = """hello world""";
let with_line_feed: str = """line feed is
preserved""";
let no_line_continutation: str = """backslash and line feed are\
both preserved""";
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 var 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 var interp_comment: str = """
This is {{ %% multiline comment ignored %% }} string content!
""";
set interp_comment = """
This is {{ % line comment ignored
}} string content!
""";
let var anything: Object = null;
set anything = .FORTY_TWO;
set anything = true;
set anything = 42;
set anything = 4.2e+1;
set anything = "forty-two";
let anything: Object = any_object;
let literally_anything: unknown = anything;
type Nullable = T?; % T | null
type Exceptionable = 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 MutableOf = mut T; % allows mutations, state changes
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)
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);
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)
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;
"" === ""; % strings are value objects
"" == "";
let and: obj = a && b; % short-circuits
let nand: bool = a !& b;
let or: obj = a || b; % short-circuits
let nor: bool = a !| b;
let pipe: bool = a |> b; % `b.(a)`, but evaluates `a` before `b`
let 'then': bool = a ~> b;
let conditional: obj = if a then b else c; % short-circuits
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
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.-3 == tuple.0;
tuple.-2 == tuple.1;
tuple.3; % TypeError
tuple.-4; % TypeError
[3, 4.0, "five"] == [3, 4.0, "five"]; % equal by component
[3, 4.0, "five"] === [3, 4.0, "five"]; % also identical by value
tuple == tuple;
tuple === tuple;
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; % TypeError
[a= 3, b= 4.0, c= "five"] == [b= 4.0, a= 3, c= "five"]; % equal by component
[a= 3, b= 4.0, c= "five"] === [a= 3, c= "five", b= 4.0]; % also identical by value
record == record;
record === record;
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: [dupe_key: bool] = [
dupe_key= true,
dupe_key= false, % Error
];
let override_count: [count: int] = [count= 2];
override_count.count == 2; % not size
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
List.<int | float | str>([3, 4.0, "five"]) == List.<int | float | str>([3, 4.0, "five"]); % equal by component
List.<int | float | str>([3, 4.0, "five"]) !== List.<int | float | str>([3, 4.0, "five"]); % but not referentially identical
list == list;
list === list; % a pointer is always referentially identical to itself
list == [3, 4.0, "five"]; % lists can be component-wise equal to tuples
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
Dict.<int | float | str>([a= 3, b= 4.0, c= "five"]) == Dict.<int | float | str>([a= 3, b= 4.0, c= "five"]); % equal by component
Dict.<int | float | str>([a= 3, b= 4.0, c= "five"]) !== Dict.<int | float | str>([a= 3, b= 4.0, c= "five"]); % but not referentially identical
dict == dict;
dict === dict; % a pointer is always referentially identical to itself
dict == [charlie= "five", bravo= 4.0, alpha= 3]; % dicts can be component-wise equal to records
let 'set': (int | float | str){} = Set.<int | float | str>([3, 4.0, "five"]);
'set'.[3]; %== true
'set'.[4.0]; %== true
'set'.[5]; %== false
'set'.count; %== 3
let literal: (int | float | str){} = {"five", 4.0, 3};
'set' == literal; % equal by component
'set' !== literal; % but not referentially identical
let map: {int | str -> str | [str] | [i: {str -> str}]} = Map.<int | str, str | [str] | [i: {str -> str}]>([
[1, "who"],
["2nd", ["what"]],
[1 + 2, [i= {"don’t" -> "know"}]],
]);
map.[1] == "who";
map.["2nd"]!.0 == "what";
map.[3]!.i == {"don’t" -> "know"};
map.count; %== 3
let literal: {int | str -> str | [str] | [i: {str -> str}]} = {
1 -> "who",
1 + 2 -> [i= Map.<str>([["don’t", "know"]])],
"2nd" -> ["what"],
};
map == literal; % equal by component
map !== literal; % but not referentially identical
let x: int = 42;
[
y= 43,
x$, % x= x
];
let list: mut List.<int | float | str> = List.<int | float | str>([3, 4.0, "five"]);
set list.[0] = "three";
list == ["three", 4.0, "five"];
let dict: mut Dict.<int | float | str> = Dict.<int | float | str>([
alpha= 3,
bravo= 4.0,
charlie= "five",
]);
set dict.[.alpha] = "three";
dict == [
alpha= "three",
bravo= 4.0,
charlie= "five",
];
let tuple: [int, float, ?:str] = [3, 4.0];
let nullish_string: str | null = tuple.2;
let record [
alpha?: int,
bravo: float,
charlie: str,
] = [
bravo= 4.0,
charlie= "five",
];
let nullish_int: int | null = record.alpha;
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 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 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: T of iterable do {
"run for each item in the iterable…
could be a tuple, list, set, or generator.";
};
let var counter: int = 0;
let expr: str = sync {
let message: str = "value";
set counter += 1;
message;
};
counter == 1;
expr == "value";
let prom: str = async {
set counter += 1;
"done";
};
prom != "done";
counter == 1;
prom~~ == "done";
counter == 2;
function my_function(x: int, y: float, z: str): bool {
return true;
}
let promise: Promise.<bool> = async { my_function.(42, 4.2, "0.42"); };
let promise_shorthand: bool^ = promise; %: Promise.<bool>
promise != true;
let awaited: bool = promise~~;
awaited == true;
let call_and_await: bool = (async { my_function.(42, 4.2, "0.42"); })~~;
call_and_await == true;
many.function~~.calls.().and.()~~.awaits.may~~.be.().chained.()~~;
type StringGenerator = Generator.<str>; %: Promise.<unknown, str>
let generator: Generator.<str> = async {
let xx: int = 42 + 2;
yield """{{ xx }}""";
let yy: float = 4.2 * 2;
yield """{{ yy }}""";
yield """{{ "hello" }}{{ "hello" }}""";
null;
};
let generator_shorthand: str* = generator; %: Generator.<str>
let xxx: [str?, PromiseStatus] = generator++;
xxx == ["44", .PENDING];
let yyy: [str?, PromiseStatus] = generator++;
yyy == ["8.4", .PENDING];
let zzz: [str?, PromiseStatus] = generator++;
zzz == ["hellohello", .PENDING];
let last: [str?, PromiseStatus] = generator++;
last == [null, .FULFILLED];
many.function++.calls.().and.()++.nexts.may++.be.().chained.()++;
function my_function(x: int, y: float, z: str): bool {
return true;
}
function func_implicit_return(x: int, y: float, z: str): bool
=> true;
let result: bool = my_function.(42, 4.2, "0.42");
let call_named: bool = my_function.(42, z= "0.42", y= 4.2);
function return_nothing(): void {
print.("void function returns nothing");
}
let not_a_value: void = return_nothing.(); %> Error
let not_a_value: unknown = return_nothing.(); %> Error
function overloaded(x: int): void {
print.("overload 1");
}
function overloaded(y: int): void {
print.("overload 2");
}
function overloaded(z: str): void {
print.("overload 3");
}
overloaded.(0); %> "overload 1"
overloaded.(x= 1); %> "overload 1"
overloaded.(y= 1); %> "overload 2"
overloaded.("10"); %> "overload 3"
let my_lambda: (int, float, str) => bool = (x, y, z) { return true; };
let lambda_implicit_return: (int, float, str) => bool = (x, y, z) => true;
function higher_order_function(fn: () => bool): void {
fn; % lambdas can be referenced uncalled
let b: bool = fn.();
}
let higher_order_lambda: (() => bool) => void = higher_order_function; %> Error: uncalled named function
function 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
function named_aliases(x= a: int, y= b: float, z= c: str): bool {
x; y; z; %> ReferenceErrors
a; b; c; % valid
return true;
};
named_aliases.(x= 42, y= 4.2, z= "0.42"); % valid
named_aliases.(a= 42, b= 4.2, c= "0.42"); % invalid!
let lambda_named_aliases: (x: int, y: float, z: str) => bool = (x= a, y= b, z= c) {
x; y; z; %> ReferenceErrors
a; b; c; % valid
return true;
};
lambda_named_aliases.(x= 42, y= 4.2, z= "0.42"); % valid
lambda_named_aliases.(a= 42, b= 4.2, c= "0.42"); % invalid!
let lambda_unnamed: (int, float, str) => bool = (x, y, z) { return true; };
lambda_unnamed.(42, 4.2, "0.42"); % valid
lambda_unnamed.(x= 42, y= 4.2, z= "0.42"); % invalid!
let x: int = 42;
f.(
44,
y= 43,
x$, % x= x
);
function myFunction(
required: int,
optional1: int = default1,
optional2: int = default2,
optional3= 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`
typefunc Box<T> => [value: T];
type S = Box.<int>;
type R = Box.<T= int>;
typefunc Or.<A, B= Bravo ?= null> = A | Bravo;
type U = Or.<int>; %: int | null
type V = Or.<B= float, A= int>; %: int | float