In MiniScript, as in some other languages, it is common to write a file that's intended to be imported as a module in some larger program — but sometimes, to be loaded and run by itself. Typically in the latter case, the point is to demonstrate or unit-test the functionality of that module.
For that to work, your code has to know whether it's being imported, or run in the main program. Today, we're going to see how that's done.
if locals == globals then...
The simple trick is to check whether locals
is equal to globals
. This is only true for code running in the global scope, that is, in the main program. Import modules run in their own scope, so for them, locals
and globals
are different.
So a common pattern you'll find at the bottom of many import modules looks like this:
if locals == globals then
// do some demo or unit test
end if
...run a demo!
A good example of this is /sys/lib/chars, which defines names for all the various special characters in the Mini Micro font. At the bottom of that file, you'll find:
if locals == globals then
text.clear
text.row = 25
print "Special characters:"
print
printAlign = function(label, text)
print " " * (30 - label.len) + label + ": " + text
end function
printAlign "left or leftArrow", leftArrow
printAlign "up or upArrow", upArrow
// (...etc...)
end if
...or unit tests!
Many of the other modules in /sys/lib have a runUnitTests
function, and the bottom of those generally looks like this:
if globals == locals then runUnitTests
You'll find this in /sys/lib/mathUtil, as well as listUtil, stringUtil, and many others. (Note that we're comparing in the other order here — globals == locals
rather than locals == globals
— which makes no difference at all.)
This trick isn't just for general-purpose libraries, either. I used it extensively in making Kip and the Caves of Lava, which consists of over a dozen MiniScript files. Most of these can be run on their own. When run in this way, each module puts its own code through its paces, so I could code/test/debug on just that file, without having to run the whole game.
When is demo also a library?
Occasionally you'll find a program that was intended primarily as a stand-alone demo, to be run and enjoyed on its own, but which can also be imported like a library module. Sometimes this is for historical reasons: something started as a demo, but it was realized that some of its classes and methods might be useful in other apps, so it was made importable.
This is done in exactly the same way as above: all the visible functionality or main program is moved into an if locals == globals
block.
Take /sys/demo/cardFlip, for example. This was originally just a stand-alone demo, but in Mini Micro version 1.2, it was refactored to be importable — a fact we used recently in making Solitaire. That entailed moving everything besides the CardSprite
class into a demo
function, and then ending with
if locals == globals then demo
So if you just load and run cardFlip, it behaves exactly as it did before; but now you can also choose to import it (after ensuring that /sys/demo is in your env.importPaths
), and use the same CardSprite
class in your own code.
Another example is /sys/demo/textAdventure. After defining a bunch of classes and methods for parsing commands, representing rooms, doors, and other objects, etc., the main program starts around line 600:
//----------------------------------------------------------------------
// Main program.
if locals == globals then
origTextColor = text.color
origBackColor = text.backColor
normalColor = color.green
text.color = normalColor
text.backColor = color.black
clear
This code goes on to create the specific text adventure The Greedy Gargoyle, which is what you'll play if you run this program. But if you want to make your own text adventure, you can import this code, and none of that main program stuff runs. You can then make use of all those support classes and functions in your own code.
No suffocating snakes here
You'll find a similar concept in some other languages. In Python, it's done this way:
if __name__ == '__main__':
But this requires that you remember not one but two magic words, __name__
and __main__
, which have little purpose anywhere else. The equivalent MiniScript trick uses no magic words; locals
and globals
are just the standard, ordinary references to local and global variables, and have many uses besides this trick.
So, the next time you see if locals == globals
in some MiniScript file, you'll know what it means. And when writing your own code, I hope you'll remember to consider whether it could be both imported and run on its own — and now you know how to make that work.