Memory Management - GoogleFeud/mafiascript GitHub Wiki
By default, mafiascript has NO real garbage collector, but also there isn't manual memory allocation / deallocation.
Shared Pointers
Every single value in mafiascript is a shared pointer that points to the value. When a value's used_count
reaches zero, the value is deallocated and the memory is freed. You don't have to deal with memory manually, almost everything is as if there is a real garbage collector. The only exception to this are nested functions. See captures to see the difference.
Here is an example:
Example
const add = (x, y) => {
return x + y;
};
add(1, 5);
In this example, add
is declared in the global
scope, so it will get deleted only when the context that ran the code above is deleted, or the global enviourment is manually cleared.
When add
gets called, a new enviourment gets created, only for this call of the function, and the values x
and y
get added to it. Once the function ends executing, the just created enviourment will be destroyed.
Captures
Captures is a list of values to "capture" inisde the function, so they become accessible to it. Since there is no real garbage collection, the interpreter must be sure that all the variables used in a non-global function are accessible and not destroyed. Captured variables live until the function they got captured in gets destroyed.
Global functions can't have captures, and global variables shouldn't be captured because they are always accessible.
Example
let globalFunc;
if (x > 50) {
let tempVariable = "1-2-3-4-5-6";
globalFunc = |tempVariable|(delimiter) => {
return tempVariable.split(delimiter);
};
}
globalFunc("-"); // [1, 2, 3, 4, 5, 6];
If some variable x
is larger than 50, a variable called tempVariable
gets created. Usually the variable will get destroyed after the if statement, but in this case it gets captured, because the function globalFunc
needs it to function. The function gets assigned to the variable globalFunc
, which is a global variable, so the function and all it's captures (tempVariable
) live forever.
You can almost always use arguments instead of captures. The only good use of captures is when you are providing callback to a function.
Example #2
Let's assume there is a global variable called array
, consisting of integers, and we don't know the length of it. We have to write a function called res
, which adds all values of the array with the number x
if the elements are more than 10, or it subtracts the number x
from every element if the elements are less than 10.
const res = (x) => {
if (array.size() >= 10) return array.map(|x|(element) => element + x)
else return array.map(|x|(element) => element - x);
};
This is an interesting case. If functions didn't have captures and they could access variables in the outside non-global enviourment, the code would be 100% valid without the capture because the function provided inside the map function gets destroyed before the variable x
does.
Here we have to capture x
if we want to use the map
function. The function we provide to map
only has access to it's own isolated enviourment (which only has an element
variable) and to the global enviourment. There isn't any memory overhead, because like I mentioned above, the function gets destoryed before the x
variable does.
Recursive functions and captures
Captures make recursive functions a little harder. If the recursion function is global, then there is no problem using the function inside itself. But if it's not, this is what you will have to do:
let recursiveFn;
recursiveFn = |recursiveFn|(...) => {...};
Alternatively, if you do not want to use captures, you can pass the function as an argument:
const recursiveFn = (_, n) => {
_(_, n-1);
};
Capture performance
Technically speaking, not only is it safe to use captures, it's also pretty fast. A function has only two enviourments: the global enviourment, and a local one. That makes variable lookup faster, because the interpreter would have to go only through 2 envioruments to get the variable. Imagine you were creating a function inside 5 nested enviourments. The interpreter would be going through 5 enviourments to find a variable that's used inside the function.