Gamma Lang: Module System

Following my previous post concerning the new programming language I am working on, I think I should actually start talking about some of the specific language features and the implementation. This post will focus on the Module System, and how including code from other files works.

Importing and not Including.

The words import and include are similar and I guess what they do in different languages has similar affects. They are however very different in implementation and the specifics of how they work. When I think about including a header, I think about C/C++. In which, you are almost cutting and pasting the code from the .h or .hpp file and plaing it into your current file. This gives you access to code that resides in a .c file that will or has been compiled into a .obj file, which is linked into your program. Without the .h file the code in your program will not know how to access the functions or variables in the linked .obj file.

Including has some nasty side effects too. These are things such as including a file twice cause a compile error (simply fixed by a '#pragma once' macro) or the fact that you must include the same header file multiple times during compilation when used in different .c files. This is because compiling C/C++ you are actually compiling each .c or .cpp file one at a time, then linking them together at the end of compilation.

Importing on the other hand (and the compilation process in gamma) compiles all of the .gma files at once. When you import a module in Gamma it will actually load it into memory, and hold it there once until the whole compilation is finished. I should also mention that there are no headers in Gamma. Declaration and Definition are one in the same. By importing a module you are actually just telling the compiler that you want access to a set of code in your current scope, and optionally aliasing it to a local identifier (for shortening long names or any other reason you might have for setting a module to local identifier).

This should make the code easier to read and allow for a much faster compilation time, as we are removing the C-like header including bits of compilation. Another way to think about the import system is by looking at Java and Python. It might even be considered a mix of the two of these, or even more similar to Pascal's 'Unit' system.

Modules for all code.

All code in Gamma must be inside of modules, no exceptions here. Your application's starting point will always need to be in a module named 'main' and a function named main, with the parameter of String[] and return of Int32. From this file you are able to import as many modules as you need and envoke any other code that you want. All of the modules you import will be compiled along with your code (modules are source only, there is not a binary form of modules) and output into your single .c & .h file set.

In a hello world example, you can see that we are already making use of the import, and we are creating a module ourself.

helloworld.gma

module main;

import std;
# import std as ::;

fun main(args : String[]) >> Int32 {
    std.writeln("Hello World, from Gamma!");
    # writeln("Hello World, from Gamma!");
    return 1;
}

As you can see already, even the standard library must be imported and called from its module indentifier. Looking at the above code I have commented out two lines, which use the std aliased into the current scope. This allows you to call the functions and variables from the module as if they were declared in your local scope. This is even possible inside of other scopes, such as functions. See the code below for and example of local imports.

hellolocalimport.gma

module main;

import std as standard;

fun main(args : String[]) >> Int32 {
    import std as ::;
    writeln("Hello Local Import!");
}

fun other() {
    writeln("other function"); # Error
    # This function does not have access to the imported std
    # since it was imported into local scope

    standard.writeln("This will work");
}

Summary!

After all of that I hope you have some sort of understanding of how the import system is designed. Well not how it works internally, but more how it should be used and how it might be useful. Once again, I am not a language designer and all of the features I am designing, are primarily for practice and to see if I can make it work correctly, or if the language feature is even useful. Imports do offer a lot of advantages over include systems, but at the cost of more memory being required. during compilation. A tradeoff which I think is most certainly worth in today's computing landscape, where the common developer's machine has 8GB or more of RAM.

Next time I hope to cover the Type system a little (It isn't implemented at all yet), and explain how Object-Oriented patterns might be used in this Psuedo OOP language.