Compilation Model

Storm is a compiled language. This means that all source code is compiled down to machine code before it is executed. The compilation process is managed automatically by Storm, so no separate compilation step is necessary.

Storm utilizes lazy compilation to guide its compilation process. At a high level, this means that Storm strives to do a minimal amount of compilation work to provide the information required by other parts of the system. As such, there is no distinct concept of compile-time in Storm, as compilation is interleaved with execution of the program. For example, when a language attempts to find a type T in a package, Storm needs to parse the relevant source files to find the available types. This happens when the system requests the information for the first time. It will, however, not populate types with member variables or function calls until that information is requested. Similarly, this work is performed whenever the information is requested by other parts of the system. Finally, functions are generally not compiled into machine code until they are called for the first time.

There are two main benefits of utilizing lazy compilation in this way. First, it reduces the start-up time of the system, since the entire system does not need to be compiled ahead of time (as of currently, Storm is not able to store compiled code to disk). Secondly, it allows running a program that contains errors, as long as the parts containing errors are not executed. This is beneficial during program development, as it allows testing early prototypes without having to address all issues up front.

The last point might come as a surprise, since most languages in Storm are strongly typed. The fact that Storm is generally strongly typed seems to imply that it would not be possible to run a program that contains errors. While this is true for languages that are compiled ahead of time, the lazy compilation model in Storm means that such errors are only found and reported when the relevant portion of the program is actually compiled. In general, this means that different errors are reported at different stages of program execution:

As we can see from above, the lazy compilation model means that it is not enough to start a program to ensure that it is free from compile-time errors. For example, creating a main function that simply prints hello world does not ensure that other functions in the same file are free from semantic errors, or that the types in the package are properly specified. This approach only ensures that the source file(s) are syntactically correct.

Because of this, it is typically beneficial to take a test-driven approach when developing programs in Storm. The tests do not have to be very rigorous as they often have to be in dynamic languages. It is enough that the test ensures that relevant functions are called, and that the relevant types are used. This will ensure that types and functions are compiled, and that any semantic errors are reported.

At certain points during program development, for example before releasing the program, it is useful to tell Storm to compile all code and report all errors. This can be achieved by calling the compile function on any named object in the system. This ensures that the object and any descendants in the name tree are fully compiled by the time that compile returns. In Basic Storm, this can be achieved as follows:

use lang:bs:macro; // Needed for the "named" macro.

void main() {
    // Ensures that "my:package" is compiled:
    named{my:package}.compile();
    // Ensures that the current package is compiled:
    named{}.compile();
}