Modules and Imports
You may have noticed that as you write code, your source file can get pretty big!
What if we could seperate certain behaviors out into their own files. Well, like with most languages, we
can use what are called modules.
What is a module
A module is just a file with code in it! Thats it!
Technically you have already been using them!
Using multiple modules with the Import statement
We can use the import statement to tell the language that
we would like to access code from another module.
stdlib and math are actually both modules! They have several functions
defined for you. Things like println are actually predefined functions.
Modules & Imports example
Let me show you an example. This example Will NOT compile.
// myModule.bcl
import stdlib::*; // import everything into the global namespace (for this module)
/// Gets the max representable number for a binary number of `x` length.
define do_a_cool_thing(x: i32) -> i32 {
return 2**x - 1;
}
// main.bcl
import myModule::*;
define main() {
do_a_cool_thing(5); // this function is not public! we can't use it!!!
}
If you try this code, it should tell you that do_a_cool_thing is private and cannot be accessed.
This is because we also need to use the public keyword. This is used on type definitions, function definitions,
and member definitions. It exposes these names to other modules, by default functions and types are private,
which means they can only be accessed in the module they were defined in. This is to prevent exposing internal workings
to every user of a module. This also lets you protect data for a variety of reasons.
Visibilty & public
public is the only visibility keyword in BCL. It is the only one you will need because by default, everything is private.
Let me show you how to use it!
// myModule.bcl
import stdlib::*; // import everything into the global namespace (for this module)
// public type
public enum MyEnum {
VARIANT1, // these don't need to be public
VARIANT2;
}
// public type
public struct MyStruct {
public a: i32, // public member
public b: i32, // public member
c: MyEnum; // private member
}
/// Gets the max representable number for a binary number of `x` length.
public define do_a_cool_thing(x: i32) -> i32 { // public function
return 2**x - 1;
}
public define new_MyStruct(a: i32, b: i32) -> MyStruct {
return MyStruct {
a: a,
b: b,
c: MyEnum::VARIANT1
};
}
// main.bcl
import stdlib::*;
import myModule::*;
define main() {
// // this wont work, c is private!
// x = MyStruct {
// a: 12,
// b: 55,
// c: MyEnum::VARIANT1
// };
// Instead we use:
x = new_MyStruct(12, 55);
// accessing public members is perfectly fine
println(x.a);
println(x.b);
// // But we CANNOT access private members
// println(x.c);
do_a_cool_thing(5); // this function IS public, we can use it!
}
Naming Collision
Sometimes, modules can define the same functions! This can cause a naming collision. This will cause the language to just pick whichever version of the function it wants to use, from whichever module it wants. This is mostly randomly.
To prevent this, we can use namespacing. Modules have namespaces that can be accessed in the same way we access enum variants.
That is by using the namespacing operator (::). This lets us specify which module we want to get the function from.
Tip
You should usually default to using namespaces and not “star imports”. The exception usually being things like the modules in the standard library.
// myModule.bcl
import stdlib::*; // "star import"
public enum MyEnum {
VARIANT1, // these don't need to be public
VARIANT2;
}
public struct MyStruct {
public a: i32, // public member
public b: i32, // public member
c: MyEnum; // private member
}
/// Gets the max representable number for a binary number of `x` length.
public define do_a_cool_thing(x: i32) -> i32 {
return 2**x - 1;
}
public define new_MyStruct(a: i32, b: i32) -> MyStruct {
return MyStruct {
a: a,
b: b,
c: MyEnum::VARIANT1
};
}
// main.bcl
import stdlib::*;
import myModule; // Not including it into the global namespace.
define main() {
// accessed using namespacing
x = myModule::new_MyStruct(12, 55);
// accessing public members is perfectly fine
println(x.a);
println(x.b);
// accessed using namespacing
myModule::do_a_cool_thing(5);
}