scratch area: free-floating ideas

indirection, mutable value semantics

When a parameter is mentioned without annotations, compiler is free to choose any way of passing an argument (high-level code is easy on details). If you want a specific way of passing a value (ref / use own / val), specify it.

TODO: a notation for mutable value semantics, most likely * to look C-like.

Linear values can be consumed and/or replaced by other value. An explicit way of passing ownership in and/or out of an automaton.

// e.g. Golang:
func (<resource>)<name>(
   <context>,
   <in> | *<inout>, ...
) (<out>...)
#![allow(unused)]
fn main() {
// e.g. Rust:
fn method(
   /* ref/use/own */ self,
   <in>,          // move the cell ownership
   &<in>,         // usually cheap way of passing values
   &mut <inout>,  // this cell cannot be consumed, values can be.
) <out>           // may be a reference!
}

THINK: bridge between linear lambda and arrays.

array value semantics

i.e. "Fortran-style programming", "data-oriented design", etc.

It's built from:

  • primitive values like u8, f32, etc.
    • can be opaque blobs of bytes.
  • arrays of values: ordered and indexed sequences of values.
  • indexes: handles to a specific array element.
    • When the array length is statically known: elements of Fin len. Can be optimized to u<N> bitsets to avoid end checking (total indexing).
    • When the array length is unknown: must carry a proof of validity.
  • slices of other arrays.

When arrays have statically known lengths: mostly solved in Ada, just have the domain of indexes as ranges.

THINK: dynamically-sized arrays, dependently-sized structs ("length-tag-value"s). This very quickly evolves into some kind of dependently-typed programming: array ranges are types dependent on the array length and values.

THINK: a generic way to project statically known things into comptime and how to fall back to dynamic checks gracefully and transparently. A generic way to uplift runtime constructs into comptime when possible.

unification and relations

i.e. relations vs functions vs automata (vs I/O?)

// append as a a prolog-style relation
rules (
   $t
   $X: $T
   exists nil : $T
   split: $X => { empty | (head $X : $t, tail $X : $T) }
   cons: ($t, $T) => $T
)
given (T: Type)
append (X: List T) (Y: List T) : List T =
   rec(self)
   match X {
   | [] => Y
   | [H, ...T] => [H, ...self T Y]
   }

It would nice to have a unified mechanism for expressing multi-way relations, not only one-way pure functions. This can be useful for typechecking, especially traits.

Think: zero or one variant unification for reversible computations. Even a two-way fork in possible options grows exponentially. But: is this practical in any way?

parameters vs context

Some variation of dynamic scoping, ideally typed and somewhat explicit.

Adapt and expand ctx context.Context Golang idiom to carry everything related to dynamic context, including capabilities. Contexts are what separates pure functions from automata.

metainterpretation

TODO: the language structure and properties should be expressed in itself.

rewriting rules

i.e. "lawful macros".

A compiler can be a bunch of rewriting rules that rewrites a given AST to a bitstring.

complex identifiers

Lisp: almost every possible character (except [(),'"]) can be a part of an identifier. Good: flexible, subsumes almost any other language. Bad: can get unreadable, especially with Unicode.

C: identfiers consist of [_A-Za-a][_A-Za-z0-9]*. Good: very minimal character set that makes it possible to highlight anywhere with anything, instantly recognizable and mostly unambiguous. Must be used as the basis of ABI. Bad: no paths, no namespaces, no structure.

Clojure: namespaced/symbols, :selfevaluated, ...

Iota: atomic identifiers made of alphabet characters and symbols. "path-like" complex identifiers like /a/b/c or a.b.c. Sigil identifiers like $a, %1, a$b, :a (atomic identifiers + punctuation without whitespace/other gaps).

errors handling: conditions?

Why do we always unwind call stack on error? Why can't we call an error handler of top of it, to be able to examine everything? Optionally it can jump into a supplied continuation with unwinding.