3.3 KiB
type system
data types:
i64, f64 - numbers bool - logical boolean values {fn [type1 type2 ... -> rettype]} / {fn [type1 type2 ...]} => [fn type1 type2 ... rettype]
{array [type length]} => [array type length]
(struct name ^type1 member1 ^type2 member2 ^type3 member3) ^{tuple [^{name member1} type1 ^{name member2} type2 ^{name member3} type3]} ^{name name} [tuple ^{name member1} type1 ^{name member2} type2 ^{name member3} type3]
(enum name (clause1 type1 type2 ...) (clause2 type1 type2 ...) clause3) {variant [^{tuple [type1 type2 ...]} clause1 ^{tuple [type1 type2 ...]} clause2 ^void clause3]}
Variables have both a datatype and an isolation modifier. There are three possible isolation types:
const
- this is the default, if no isolation modifier is given. No in-place mutations are possible withconst
values.val
- aval
variable can be mutated in-place, but changes only affect that variable. If it is assigned to any other variable or passed as a parameter, it is copied if necessary and can be treated as a new, totally distinct value.mut
- Only valid on function parameters. Denotes a value in which mutations to the parameter are visible from the calling function. Any assignments of amut
value to other variables (except being further passed as amut
parameter) makes a copy.
All datatypes can have the following modifiers:
ref
- aref
is analogous to a full pointer or object reference. Copies of the sameref
can exist in multiple places, and refer to the same object in memory. Changes to data mutated via aref
are immediately visible to any other code that has the sameref
.ref
s are created with thebox
function. References that point in the middle of a structure are not possible; use a tuple or an enum or something.opt
- equivalent to(enum [opt type] (some type) none)
, assuming we had generics, which atm we do not. no idea what destructuring helpers make sense here yet.array
- contiguously-allocated values of any type. size can be dynamically specified, bounds are checked on access.
Q: should const
structures be able to modify ref
members directly? leaning towards yes. ref
seems to inherently imply
interior mutability - after all, the value a given ref
points to is not constant.
Interesting thought: we only need GC for ref
s! local variables + parameters can live in a stack-based arena, and
globals are statically allocated.
typed dynamic dispatch
(protocol name (method name [^mut self ^type1 arg1 ^type2 arg2 -> rettype]))
(impl protocol type (method name [^mut self ^type1 arg1 ^type2 arg2 -> rettype] body...))
type restrictions
scoped
- would be nice to be able to define destructors and have some kind of RAII, but it's probably overkill
memory management
- "hot reload" implies "given the previous source code and a memory, I can reason about the types of everything in the memory"
- compacting garbage collection is simply the degenerate case of rearranging memory to be legible to new code!
- we have typed roots (globals), and we can follow typed references from there
- stack is not accessible from wasm, and GC / reload would only happen when wasm code returns to JS - no suspension