MiniScript does not have a standard way to include some other file or module into your code at runtime. Mini Micro, however, adds an import
function for this purpose. How it works is a little tricky. It may help to first understand the goals:
- Code in the imported module should not just dump its stuff into the global namespace, but should instead be accessible under the name of the module.
- While you're developing a module, you should be able to load and run it on its own, and it should still work.
So this is not like #include
in C/C++. It is much more like import in Python.
We accomplish this by wrapping the imported code in a function, and at the end of that function, returning its local variables as a map which then gets stored under the module name. The following code uses essentially the same trick, without using import
, to illustrate:
// Here is how import works, under the hood.
// The calling code says:
// import "foo"
// ...and this creates a function that contains all
// the imported code:
fooImportFunc = function()
// Here is the guts of "foo". It can define a constant...
pie = "yummy"
// and a method...
describe = function(howGood)
return (pie + " ") * howGood + " pie"
end function
// and another function that references the first
offer = function()
print "Have some " + describe(2) + "!"
end function
// And some global code.
cake = "Almost as " + pie + " as pie"
// And then all this gets returned as locals.
return locals
end function
// And then back in the caller, we store the result
// of that call under the import name.
foo = fooImportFunc
// Now we can do stuff like:
print foo.pie
foo.offer
So this accomplishes our goals. It relies on the (general MiniScript language) feature that you can define a function inside a function, and when you do, you have access to that larger function's local variables from within the embedded function.
The actual import file in this example would be everything between the comments "Here is the guts" and "And then all this gets returned" above. That file could be run on its own. And though I haven't tried it, if you wanted to do something different when loaded as a module vs. run on its own, I think you could easily distinguish them by checking if globals == locals
(which will be true when run on its own, and false when imported as a module).
How all this works has some implications:
- a module can, if you want, stuff things directly into the global namespace, by simply assigning to globals.whatever
- however, a function module cannot assign to something at the module scope; normally you'd do that via globals.whatever, but now (see bullet point above) that creates something in the global scope, not the module.
And it's this last bit that has me still 1% unhappy. Consider the following module.
useCounter = 0
useIt = function()
print "You use the thing."
useCounter = useCounter + 1 // <----- ?!?!?
end function
This does not work, because on the indicated line, we're just creating a variable local to the useIt function, which goes poof as soon as we exit that function. In normal MiniScript code, the simple and standard solution would be to assign to globals.useCounter instead. That'll work if this code is run on its own, but not when it's a module, as that would assign to the global useCounter, not the one defined within the module.
The basic problem is that we now have a nested scope, with no way to directly reference the set of variables one level up. To solve this, we will probably need to add something new to the language... something similar to locals
and globals
, but which means "the locals of the function this function was defined inside, or if it was defined at the global scope, then the globals". That's a lot of meaning to cram into an identifier. Maybe module
? So then you could write
module.useCounter = useCounter + 1
and that would work. But I don't love it. Sometimes this situation has nothing to do with modules; it can arise any time you're using a nested function. In this RosettaCode example, I had to make my counter a list so that it would be mutable and I could change its value within the embedded function. I'd much prefer to use the new identifier to just refer to and update an ordinary numeric value... but calling it module
doesn't make much sense in this case. And that's why I haven't done it yet.
Whew! That's a lot to absorb. But I really am kind of stuck on this one, and any feedback is very welcome. 🙂