Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overview

import {
    std { compare.Ordering, io.stdin },
    random { Random, thread_rng },
};

func read_number(buffer: $String) -> Result[U32, ParseError] {
    buffer.clear;
    stdin().read_line($buffer).expect;
    buffer.trim.parse
}

func main() {
    let mut buffer = String.new;

    print("Please enter a number: ");

    let number = while_failing {
        read_number($buffer)
    } {
        print("Input must be a number.");
    };

    let correct = 0.to(number).random(thread_rng());
    print("Guess a number 0 to \(number): ");

    loop continue!{
        let guess = read_number($buffer).catch {
            print("Input must be a number.");
            continue!();
        };

        correct.compare(&guess).match {
            Ordering.Greater -> print("Higher..."),
            Ordering.Less -> print("Lower..."),
            Ordering.Equal -> {
                print("Correct!");
                return!();
            },
        };
    };
};

Inspirations

Major Inspirations

LanguageInspiration
Rustphilosophy, syntax, features
Gleamphilosophy, syntax, features
Smalltalkphilosophy, features
Swiftsyntax, ergonomics

Gleam was only discovered by me after significant design work for Sonance was already complete. Still, they two languages are very similar! That is why Gleam is listed as a major inspiration.

Minor Inspirations

LanguageInspiration
Kotlin
Inko
Kokabackpassing
Rubyblocks

Bibliography

Vscode Extension / TextMate Grammar

Unsorted

Language Reference

Keywords

ContextKeywordDescription
ModulesmoduleDeclare modules or implementations
ModulesimportImport items from other modules
ModulesexportMake item available to rest of current package
ModulespublicMake item available to external packages
TypestypeDeclare or alias types
TypestraitDeclare or alias traits
FunctionsfuncDeclare functions
FunctionsdoDeclare arguments in a block literal
FunctionsblockBlock type literal
PatternsletDeclare variables
PatternsmutMake place mutable
PatternssetAssign value to place
PatternsmatchPattern match an value

Punctuation

NameSymbolDescription
=Assignment, labeled parameters
.Pipeline
;Statement ending
,General purpose separator
:Type ascription
()Parameters, arguments, grouped expressions
{}Code blocks
[]Type parameters / generics
!Label declaration, label call
?Implicit argument, backpassing
->Return type ascription, match branches
&Shared references
$Unique References

Strings

Escape Codes

See Rust lol.

String Interpolation

Interpolation can be done with a special escape sequence \(...) where the ... is an arbitrary expression.

print("\(a) plus \(b) is \(a + b)");

Multiline Strings

Both after the opening quote and before closing quote must be only whitespace, which is stripped. Starting whitespace of the closing quote is commonly stripped from all middle lines, syntax error if a line has less starting whitespace. Newlines can be escaped using \ at the end of the line.

let example = "
    foo\
    bar
    ";

assert_equal(example, "foobar");

Raw Strings

Raw strings are created by prefixing the opening quote and suffixing the closing quote with a set number of # characters. Within raw strings, normal special characters are interpreted literally, including sequences of lesser amounts of #. To use escape sequences, prefix the \ character with the same amount of # as the opening and closing quotes.

let example ###"
    "you can put whatever here"
    ##" not affected
    ###\n escape codes
    ###\(value) interpolation works
"###;

Functions

func add(x: U32, y: U32) -> U32 {
    x + y
}

Modules

Basic Modules

module my_module {
    ...
}

Basic modules with no body can be used as a file heading. A file may have zero or one. This allows no identifier, instead using the file name. This is so you may attach attributes or documentation to a module declared by a file.

module;

Type Modules

Types themselves can act as modules, and you may declare and access items inside of them. To declare items as apart of a type, use the type keyword along with the type name instead of a module identifier. This may only be done within the file a type is defined, and cannot be used on type aliases.

module type MyType {
    const num = 123;
}

print(MyType.num)

Trait Modules

Modules can implement a trait for a given type using ->. This must provide implementations of any items the trait requires, and will automatically provide them whenever the type is used in a context that requires that trait. Like inherent type modules, they contain the type keyword instead of a module identifier and must be declared in the same file as the type or the trait.

module type MyType -> MyTrait {
    ...
}

func use_trait[T: MyTrait](input: T) {
    ...
}


let value: MyType = ...;
use_trait(value);

Named Trait Modules

What if you needed to declare a trait module outside of where the type or trait is defined? Or if you want to provide an alternative to an existing trait module? In that case, you may provide a module name when creating a trait module. In this case, those items are only used when specifically requested. This also prevents the “HashMap problem” familiar to Rust’s justification of it’s orphan rules system.

module foo MyType -> MyTrait {
    ...
}

func use_trait[T: MyTrait](input: T) {
    ...
}

let value: MyType = ...;
use_trait(foo(value)); // Apply the named trait module before passing it

Types

Custom Types

type MyType {
    VariantA,
    VariantB,
}

Records

type MyRecord(foo: U32, bar: Boolean);

Anonymous Records

fn sincos(x: F64) -> type(F64, F64) {
    type(x.sin, x.cos)
}

Type Aliases

type Alias = MyType;

Blocks

Example: scope

func scope[T](body: block() -> T) -> T {
    body!()
}

Example: Option[T]

condition
    .then { print("was true") }
    .else { print("was false") }
func then[T](self: Boolean, body: block() -> T) -> Option[T] {
    match(self) {
        True -> Some(body!()),
        False -> None,
    }
}

func else[T](self: Option[T], body: block() -> T) -> T {
    match(self) {
        Some(value) -> value,
        None -> body!(),
    }
}

Example: while

let mut i = 1;

while { i < 6 } {
    print(i);
    i = i + 1;
}
func while(condition: block() -> Boolean, body: block()) {
    loop {
        condition!().then {
            body!();
        };
    };
}

Labels

Backpassing

Function based

Monad based

Uses With Containers

func maybe_sum(x: Option[U32], y: Option[U32]) -> Option[U32] {
    x.flat_map do(i) {
        y.flat_map do(j) {
            Some(i + j)
        }
    }
}
func maybe_sum(x: Option[U32], y: Option[U32]) -> Option[U32] {
    let i = x.flat_map?;
    let j = y.flat_map?;

    Some(i + j)
}

Backpassing match Special Form

match(foo) {
  True?,
  False -> print("nope"),
};

print("yep");

Expression returns a tuple.

With values

match(foo) {
  Pass(value)?,
  Fail(error) -> print("oh no: \(error)"),
};

print("yippee: \(value)");

Traits

Advanced Modules

Week of 2025-09-08

Sunday 07

Converting this repository from solely housing the “book” into being a general purpose monorepo for the project. It makes sense I feel to keep everything in once spot, and the existing “book” is enough of a something to work from. Speaking of that, the “book” has gone from purely design notes to general purpose documentation. Now housing the starts of a bibliography (that I desperately need to organize), as well as this devlog, which should prove at the very least interesting in a few years, if not helping me gain support.

I also spent some time today starting the vscode extension, which gives us helpful things like syntax highlighting and basic convince features, but also will be an important stepping stone in getting the language server set up. This took some fumbling to get started and will likely take a lot of trial, error, and research to continue. Yay.

Tuesday 09

I spent sometime in class today cleaning up the design notes from the previous “version” of the “book”. I made the kinda whim decision of moving generics from <> to []. There’s plenty of logical and philosophical reasons why you can find online, I suppose. I figured I wouldn’t be used to it at first but I’m surprised it looks rather nice. I’m using the GitHub online vscode editor thing, to get around windows-linux file issues, but it doesn’t support things like formatting or spell checking. I remember something along the lines of .gitattributes I should look into. My next tasks are improving the design notes, especially the tokens reference.