I am creating a language which is compilable to Swift, Rust, and JavaScript (or at least trying). Rust and Swift both use pointers/references/dereferencing/etc., while JavaScript does not. So in a Rust-like language, you might do something like this:
fn update(x) {
*x++
}
fn main() {
let i = 0
update(&i)
log(i) #=> 1
}
In a JavaScript-like language, if you did this then it would fail:
function update(x) {
x++
}
function main() {
let i = 0
update(i)
log(i) #=> 0
}
Because the value is cloned as it is passed in (as we obviously know).
So what I am thinking about is doing this at first:
function update(scopeWithI) {
scopeWithI.i++
}
function main() {
let i = 0
let scopeWithI = { i }
update(scopeWithI)
i = scopeWithI.i
log(i) #=> 1
}
But that is a lot of extra processing going on, and kind of unnecessary it seems. Instead I might try compiling to this:
function update(scopeWithI) {
scopeWithI.i++
}
function main() {
let scope = {}
scope.i = 0
update(scope)
log(scope.i) #=> 1
}
This would mean every nested scope you create, you would have to start manually creating/managing the scope chain. And actually that wouldn’t work because update
is hardcoded to i
. So you might have to pass in what the variable name is you want.
function update(scope, ...names) {
scope[names[0]]++
}
But then it’s like:
function update(scope, ...names) {
scope[names[0]]++
}
function main() {
let scope = {}
scope.i = 0
if random() > 0.5
let childScope = { scope }
childScope.x = 0
update(childScope, ['i'])
update(childScope, ['x'])
update(childScope, ['x'])
log(childScope.x) #=> 2
else
update(childScope, ['i'])
log(scope.i) #=> 1
}
So that seems like it might get us somewhere.
So then it’s like, the generic solution is to have scope be the first parameter to a function.
function add(scope, name1, name2) {
return scope[name1] + scope[name2]
}
Dereferencing means reading a value directly from the scope, while passing a reference (like &name
in Rust or C), would mean passing the scope and the name.
Will something like this work? Or better put, what needs to be changed or added? Does it need to get any more complicated than this?
I would like to try and find a way to transform the pointer-oriented code into JavaScript (transpilation), without at first trying to figure out the seemingly much more complicated approach of not being so direct, and avoiding pointer simulation in JavaScript by redefining a lot of the methods. It seems that avoiding any pointer use in JavaScript would be way harder to figure out, so I am trying to see if a pointer sort of system would be possible to simulate in JavaScript.
To avoid pointer simulation, you would have to redefine methods.
update(x) {
*x++
}
Would have to change the outer usage of the function everywhere. So this:
main() {
let i = 0
update(&i)
}
Would become:
main() {
let i = 0
i++ // inline the thing
}
For this simple case it’s fine, but for a more complicated function it starts to seem like macros and might get complicated.
So instead of changing the outer usage, we make it so you have to pass the scope.
Another approach might be to have every variable be an object with a value, so it’s more like:
update(x) {
x.value++
}
main() {
let i = { value: 0 }
update(i)
}
So then I’m thinking to myself, how to handle references to references then?
update2(x) {
update(&x)
}
update(x) {
*x++
}
main() {
let i = 0
update2(&i)
}
In the system i described, that would be like:
update2(x) {
// then what?
let y = { value: x }
update(y)
}
update(x) {
// like this?
x.value.value++
}
main() {
let i = { value: 0 }
update2(i)
}
So it seems this wouldn’t really work.