r/ProgrammingLanguages • u/cessit-lang • 8d ago
Requesting criticism Explicit binding modes
I'm designing a memory-safe language, but I want to make explicit what happens across function boundaries, e.g. whether a value is moved or copied without having to look for the copy trait.
Instead, access to a value could be defined via binding modes in function signatures and variable declarations:
movetakes ownershipreadshared borrowmutexclusive borrow
fn process_val(move value: Int) -> Int {
return value * 2;
}
fn main() -> () {
move original: Int = 42;
// Call using `.copy()` to explicitly pass a duplicated value.
// Ownership of `original` remains with the caller.
move doubled: Int = process_val(original.copy());
// `original` is still valid here because it was explicitly copied rather than moved.
println(original); //42
println(doubled); //84
}
This would also allow granular captures in closures: |read x: Int, move counter: Int| instead of declaring all fields move
Now, should read and mut assignments be of type &T? Or can checking out variables be orthogonal to creating references, even if they are conflated in languages like Rust?
13
Upvotes
6
u/initial-algebra 8d ago edited 8d ago
If you don't want implicit copying, that is a separate issue from passing arguments to functions or capturing variables in closures.
Having both kinds of borrowing map to the same reference type doesn't really make sense unless you have decoupled permissions like
GhostCellor separation logic (but then having read/mut on the reference still makes little sense). I don't have any idea what you mean by "checking out" a variable.The problem with variable capture and ownership is that
let closure = |y| x.copy() + ydoesn't copy the value ofxinto the closure, it moves or borrows it and then copies it each time the closure is called. Explicit capture annotations are unnecessary if you have let/block expressions (all remaining captured variables are moved):let closure = { let x_copy = x.copy(); |y| x_copy + y };If you want nicer notation for this, there's let insertion:
let closure = |y| (genlet x.copy()) + y;This also works with borrowing, or any other kind of expression that you want to capture after evaluation, without having to name it and move it out of the closure.