Bumping the version
principles
// non-ref to non-ref
[self example] [:Person | name: 'B'].
person := {Person | name: 'A'}.
person example.
person name. // 'A'
// non-ref to ref
[self example] [:&Person | name: 'B'].
person := {Person | name: 'A'}.
person example.
person name. // 'B'
// ref to non-ref
[self example] [:Person | name: 'B'].
person := {Person | name: 'A'}.
(&person) example. // force person to be a reference
person name. // 'A'
// non-ref to non-ref chosen
[self example] [:Person | name: 'B'].
[self example] [:&Person | name: 'C'].
person := {Person | name: 'A'}.
person example.
person name. // 'A'.
// ref to ref chosen
[self example] [:Person | name: 'B'].
[self example] [:&Person | name: 'C'].
person := {Person | name: 'A'}.
(&person) example.
person name. // 'C'.
[self capitalised-name] [:Person -> :String | name to-uppercase].
[self capitalised-name: new-name] [:&Person | name: new-name to-uppercase].
syntax
strings
'string'
"string"
`string`
regular-string: String = "Hello World".
escaped-string: EscapeString = "Hello\tWorld\n".
template-string: TemplateString = "Hello [name]".
booleans
true
false
integers
1234567890
-1234567890
1_234_567_890
-1_234_567_890
0b0110101001
0o71727375
-0o71727375
0xF23BEEF
-0xF23BEEF
floats
1234.56789
-1234.56789
1_234.567_89
-1_234.567_89
comments
// single line comment
statements
subexpressions
1 * (2 + 3).
1.5 * (Float | 2 + 3).
Celsius :: {temperature: Number}.
[self cast] [:Number -> :Celsius | {temperature: self}.
room-temperature: Celsius = 20.
// transforms into:
// room-temperature: Celsius = {temperature: 20}.
variable declaration
my-name: String
variable assignment
my-name: String = "Fallen"
variable assignment with type deduction
my-name := "Fallen"
constant declaration
my-name :: "Fallen"
object creation
{Person | name: "Bob", age: 27, jump}.
bob: Person = {name: "Bob", age: 27, jump}.
object focus
bob := {Person | name: "Bob"}.
bob {age: 27, height: 175}.
// the focused block is equivalent to:
// bob age: 27.
// bob height: 175.
type declarations (classes)
Person :: {name: String}.
Person :: {name: String = "Fallen"}.
Person :: {name: String, age: u64}.
Person :: {name, nickname: String, age: u64}.
#public as it is the default.Person :: {name: String, #module has-changed: Bool}.
FunPerson :: {...Person, fun-level: u64}.
AI :: {...Person, ...Machine, intelligence: u64}.
// A simple distinct type
UUIDv7 :: {value: u128}.
// The base library facility for distinct types
DistinctType ::
{#module #generic origin-type: Type,
#module value: origin-type}.
// Creating the DistintType from the origin-type:
[self cast] [self: Type -> DistinctType[self] | {value: self}].
// Getting the origin-type back from the DistinctType:
[self cast] [self: DistinctType -> self origin-type | value].
// Using it:
UUIDv7 :: DistinctType[u128].
List ::
{#generic element-type: Type,
origin: MemoryAddress[element-type]}.
[list do-something] [:&List -> :String | #required].
[list do-something-else] [:&List -> :String | 'Hello World: List'].
SubList :: {...List[u64]}.
[sublist do-something] [:&SubList -> :String | 'Hello World: SubList'].
[sublist do-something-else] [:&SubList -> :String | #deleted].
tagged unions
FoodOrNotFood :: Food + NotFood
enumeration declaration
cardinals :: {north, east, south, west}.
cardinals :: {north = 1, east, south = 8, west}.
cardinals :: {Integer | north = -1, east, south, west}.
arrays
{}
{1, 1 + 1, 3}
{Float | }
{Float | 1, 2, 3}
group-a := {1, 2, 3}.
group-b := {4, ...group-a, 5}.
{1, 2, 3, 4, 5}[1:3]. // {2, 3, 4}
{1, 2, 3, 4, 5}[1:]. // {2, 3, 4, 5}
{1, 2, 3, 4, 5}[:3]. // {1, 2, 3, 4}
{1, 2, 3, 4, 5}[::3]. // {1, 2, 3}
{1, 2, 3, 4, 5}[2::2]. // {3, 4}
{1, 2, 3}[2].
people[0] = {name: "Zero", age: 0}.
maps
{"A": 64, "B": 65, "C": 66}.
{String, Integer | }.
{String, Integer | "A": 64, "B": 65, "C": 66}.
group-a := {"A": 64, "B": 65, "C": 66}.
group-b := {"Z": 0, ...group-a, "X": 1}.
a-character := "A".
a-value := 65.
group-a := {(a-character): a-value}.
group-b := {...(group-a)}.
{"A": 65, "B": 66}["B"].
{"A": 65, "B": 66}["B"] = 67.
reference type
PersonRef :: &Person.
method sends
person name
1 + 2 * 3
{1, 2, 3} merge-with> {2, 3, 4} print-into> stdout
gc draw-line-from: {0, 0} to: {15, 15}
| precedence | thing |
|---|---|
| 9 | unary method sends |
| 8 | * / % & |
| 7 | + - ~ |
| 6 | = ≠ < > ≤ ≥ |
| 5 | && |
| 4 | ││ |
| 3 | [index] [start:stop] [start::length] |
| 2 | binary method sends |
| 1 | keyword method sends |
| 0 | reference & |
methods
[foo bar] [:Integer -> :String | ].
[foo bar] [foo: Integer -> bar: String | ].
[foo bar: baz and: qaz] [:Integer, :Integer, :Integer -> :String | ].
[foo bar: baz and: qaz] [foo: Integer, baz: Integer, qaz: Integer -> blah: String | ].
[foo + baz] [:Integer, :Integer -> :String | ].
[foo bar> baz] [:Integer, :Integer -> :String | ].
[self print-my-name] [:Person | name print].
[foo bar] [foo -> bar | ].
[foo bar: baz] [foo, baz -> blah | ].
Animal :: {}.
Cat :: {...Animal}.
Dog :: {...Animal}.
[living-creature my-method] [:Animal |
[cat switch] [:Cat | cat chase-yarn].
[dog switch] [:Dog | dog chase-ball].
[animal switch] [:Animal | animal stare-intently].
living-create switch].
{animal_switch, cat_switch, dog_switch}[living-creature type-id] evaluate: living-creature value.
blocks
[:Integer -> :String | ].
[foo: Integer, bar: Integer -> baz: String | ].
[:String | ].
[ -> :String | "Hello World"].
[foo, bar | ].
[foo, bar -> baz | ].
people select> [age ≥ 18] do> [person | stdout print: person].
[a: String, b: Integer -> return | return <- a + b print].
[+ 1] evaluate: 2. // answer is 3
people select> [age ≥ 18]. // all the adults
return values
[self my-method] [:Person -> method_return: Integer |
self do-something: [:Person -> block1_return: Integer |
self do-something-else: [:Person -> block2_return: Integer |
// leave this block
block2_return <- 20000.
// leave the parent block
block1_return <- 2000.
// leave the method
method_return <- 200.
// the block ends naturally
20].
// leave this block
block1_return <- 1000.
// leave the method
method_return <- 100.
// the block ends naturally
10].
// leave the method
method_return <- 1.
// the method ends naturally
0]
words do: [index, word -> return |
word = "soup" then: [
foundIndex := index.
return <-]].
[self save] [:&DatabaseRecord -> return |
has-changed else: [return <-].
write-to-database]
foundIndex := words
do> [index, word -> return |
word = "soup" then: [return <- index]]
else: -1.
base library
Boolean
4 is-even then: ["It's even" print].
4 is-odd else: ["It's even" print].
4 is-even
then: ["It's even" print]
else: ["It's odd" print].
true && false. // false
true || false. // true
true not. // false
Maybe
// with boolean operations
maybe then: [value | ... ].
maybe else: [ ...no value... ].
maybe else: default-value.
// with binary operations
maybe then> [value | ... ] else> [ ...no value... ].
// violently
maybe value-or-panic.
Result
// Make a new kind of Result
OutOfMemory :: {...Result}.
memory := (allocator allocate: 5 bytes) else> [:OutOfMemory | "We ran out of memory" panic].
// Make many new kinds of Result using an enum.
FileOpResults :: {Result | OK, FileNotFound, FileAccessDenied}.
contents := "test.txt" as-filename open-read
else> [:FileNotFound | "Where is it?" panic]
else> [:FileAccessDenied | "We cannot read that one" panic]
then> [get-contents].
Either
[filename open-read] [:Filename -> :FileHandle / FileOpResult | ... ].
// with boolean operations
either then: [truth | ...].
either else: [non-truth | ...].
either else: default-value.
// with binary operations
either then> [truth | ...] else> [non-truth | ...].
// violently
either then-value-or-panic.
either else-value-or-panic.
Numbers
Memory
home := {MemoryAddress | base: 0}
// read from the start of memory
home[0].
// write to the start of memory
home[0] = 0xFF.
// create a new address offset from home
home + 0xDFFF.
[MemoryAddress base: offset]
[:Class, :UnsignedInteger -> :MemoryAddress |
self new {base: offset} // calling new, creating an instance on the heap].
home := MemoryAddress base: 0. // allocated on the heap somewhere of type &MemoryAddress[u8]
home free. // release the allocated memory
[person grow-older] [:&Person -> ø | age: age + 1].
memory-arena: HeapAllocator.
people: List[Person] = {allocator: memory-arena, grow-to-atleast: 1}.
people[0] grow-older. // auto-referenced to match the grow-older method
people free // return object space back to the memory-arena, unncessary given the next call
memory-arena free // free the entire arena, people becomes invalid
Collections