PureBasic Modules: A Quick Tutorial
Posted: Thu May 10, 2018 6:19 pm
A QUICK & SIMPLE TUTORIAL ON THE USE OF MODULES
1. THE BASICS
The PureBasic manual defines modules as follows:
Let's demonstrate this with some simple examples. Consider this empty procedure:
The module equivalent of the above procedure would be these set of closures:
At this point, they both do absolutely nothing. So, let's add a variable to the procedure:
We know that the variable myVar is not accessible from the parent program, and trying to access it would only generate a new variable of the same name in the parent program, resulting with a zero value:
The correct way of accessing the variable in the procedure would be to instruct the procedure to deliver the value to the parent program, like so:
Quite simple and basic stuff.
In the case of modules, there would be two ways to add variables. This is the first way:
And this would be the second way:
What's the difference? Before we answer that, let's take another look at the basic structure of the module:
As the function name implies, the upper DeclareModule section handles the module declarations, while the lower Module section houses all of its functions. However, all the code in the module section would remain locked and inaccessible to the parent program unless they are selectively and expressly declared in the upper declaration section. To illustrate this further, let's return to the earlier example about the variable.
In the first of the two module examples, the variable myVar was declared in the lower module section, like so:
Like the procedure earlier, variables in modules are also not accessible from the parent program, and trying to access them would only generate a new variable of the same name in the parent program, resulting with a zero value:
Unlike procedures, which would have to return the value of the variable to the parent program, variables in modules can be referenced and accessed directly. The syntax for referencing variables in modules from the parent program would be to prefix them with the module name, as follows:
However, you'd notice that the resulting output is still zero. Why is this?
As stated earlier, variables and functions enclosed within modules are not accessible by the parent program unless they are expressly made available. And this is done by declaring them in the upper declaration section of the module structure.
In order to remedy this, and make the variable accessible, we would have to use the second method, which declares the variable myVar, like so:
Furthermore, the values for these public variables need not be assigned in the declarations section, and could also be assigned in the module body, like so:
So, what else could we do with modules?
Procedures are great at modulating reusable functions, saving the need for tediously repetitive code. However, at best, each procedure is capable of performing only a handful of processes. Consider this:
The module-equivalent of the above procedure would look like this:
Notice how the same procedure has been added into the module, and notice how it has also been declared in the upper declaration section. It is then called in the same method, by prefixing the module name to the procedure name.
At its very core, that's all there is to modules. A glorified procedure with its own namespace. Although that's where the similarities between procedures and modules end.
2. INNER FUNCTIONS
As mentioned earlier, modules are like mini-programs. In addition to their exposed variables and functions, they could also house private ones that are never accessed or referenced by the parent program. For example:
The variable myPrivateVariable and the function myPrivateFunction() are both inaccessible to the parent program, but they are still able to work within the module itself, called and operated upon by its own functions. A slightly oversimplified example, but it's pretty straightforward.
3. NAMESPACES
To delve further, let's explore the concept of namespaces. We've seen that modules have a unique prefixing syntax with which to reference its elements.
However, there's another way to reference them, provided there are no naming conflicts with the parent program. There is a special closure function that can be used to omit the module prefix when used in the parent program. It's called UseModule/UnuseModule, and the implantation syntax is as follows:
That's really convenient, with one caveat; this convention would fail if the parent program utilises even one identical identifier for any of its variables or functions. In such a case, the UseModule/UnuseModule syntax would not be permitted, so use the module prefix instead.
4. INTEROPERABILITY
Now that we've seen how the module makes its variables and functions available to the parent program, how do we get elements from the parent program into the module?
The simple answer is, this is not possible. Not directly, anyway.
To do this, a bridging interface would be required. And for that, we would simply use another module. The manual refers to this as a common module, but it's really just a dummy module whose sole purpose is to relay data between the parent program and the modules in use. It's pretty simple actually.
And that's it! That about covers the basic aspects of modules in PureBasic. I hope that it has been clear and understandable, and at the very least, clarifying some of the common confusions.
Your feedback and comments are always welcome.
1. THE BASICS
The PureBasic manual defines modules as follows:
Technically, modules are mini-programs within larger programs, which could be called like procedures. But unlike procedures, they execute their immediate code without the parent program calling them. And also unlike procedures, modules could also contain their own procedures.Modules are an easy way to isolate code part from the main code, allowing code reuse and sharing without risk of name conflict. In some other programming languages, modules are known as 'namespaces'.
Let's demonstrate this with some simple examples. Consider this empty procedure:
Code: Select all
Procedure myProcedure()
EndProcedure
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
EndModule
Code: Select all
Procedure myProcedure()
Protected myVar = 123
EndProcedure
Code: Select all
Procedure myProcedure()
Protected myVar = 123
EndProcedure
Debug myVar ; == 0
Code: Select all
Procedure myProcedure()
Protected myVar = 123
ProcedureReturn myVar
EndProcedure
Debug myProcedure() ; == 123
In the case of modules, there would be two ways to add variables. This is the first way:
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
Define myVar = 123
EndModule
Code: Select all
DeclareModule myModule
Define myVar = 123
EndDeclareModule
Module myModule
EndModule
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
EndModule
In the first of the two module examples, the variable myVar was declared in the lower module section, like so:
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
Define myVar = 123
EndModule
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
Define myVar = 123
EndModule
Debug myVar ; == 0
Code: Select all
DeclareModule myModule
EndDeclareModule
Module myModule
Define myVar = 123
EndModule
Debug myModule::myVar ; == 0 ?
As stated earlier, variables and functions enclosed within modules are not accessible by the parent program unless they are expressly made available. And this is done by declaring them in the upper declaration section of the module structure.
In order to remedy this, and make the variable accessible, we would have to use the second method, which declares the variable myVar, like so:
Code: Select all
DeclareModule myModule
Define myVar = 123
EndDeclareModule
Module myModule
EndModule
Debug myModule::myVar ; == 123
Code: Select all
DeclareModule myModule
Define myVar
EndDeclareModule
Module myModule
myVar = 123
EndModule
Debug myModule::myVar ; == 123
Procedures are great at modulating reusable functions, saving the need for tediously repetitive code. However, at best, each procedure is capable of performing only a handful of processes. Consider this:
Code: Select all
Procedure myProcedure(a, b)
c = a + b
ProcedureReturn c
EndProcedure
Debug myProcedure(1, 2) ; == 3
Code: Select all
DeclareModule myModule
Declare myProcedure(a, b)
EndDeclareModule
Module myModule
Procedure myProcedure(a, b)
c = a + b
ProcedureReturn c
EndProcedure
EndModule
Debug myModule::myProcedure(1, 2) ; == 3
At its very core, that's all there is to modules. A glorified procedure with its own namespace. Although that's where the similarities between procedures and modules end.
2. INNER FUNCTIONS
As mentioned earlier, modules are like mini-programs. In addition to their exposed variables and functions, they could also house private ones that are never accessed or referenced by the parent program. For example:
Code: Select all
DeclareModule myModule
Define myVariable
Declare myProcedure(a, b)
EndDeclareModule
Module myModule
myVariable = 123
myPrivateVariable = 789
Procedure myPrivateProcedure()
Shared myPrivateVariable
Debug Str(myPrivateVariable)
EndProcedure
Procedure myProcedure(a, b)
myPrivateProcedure()
c = a + b
ProcedureReturn c
EndProcedure
EndModule
3. NAMESPACES
To delve further, let's explore the concept of namespaces. We've seen that modules have a unique prefixing syntax with which to reference its elements.
Code: Select all
myModule::moduleVariable
myModule::moduleFunction()
Code: Select all
DeclareModule myModule
Define myVariable
Declare myProcedure(a, b)
EndDeclareModule
Module myModule
myVariable = 123
Procedure myProcedure(a, b)
c = a + b
ProcedureReturn c
EndProcedure
EndModule
UseModule myModule
Debug myVariable
Debug myProcedure(1, 2)
UnuseModule myModule
Code: Select all
myVariable = 456
DeclareModule myModule
Define myVariable
EndDeclareModule
Module myModule
myVariable = 123
EndModule
;UseModule myModule ; this entire block would
; Debug myVariable ; throw a namespace error
;UnuseModule myModule
Debug myVariable ; == 456
Debug myModule::myVariable ; == 123
Now that we've seen how the module makes its variables and functions available to the parent program, how do we get elements from the parent program into the module?
Code: Select all
Global myVersionNumber = 1.23
DeclareModule myModule
Declare displayVersionNumber()
EndDeclareModule
Module myModule
Procedure displayVersionNumber()
Debug myVersionNumber
EndProcedure
EndModule
myModule::displayVersionNumber() ; == 0 ?
To do this, a bridging interface would be required. And for that, we would simply use another module. The manual refers to this as a common module, but it's really just a dummy module whose sole purpose is to relay data between the parent program and the modules in use. It's pretty simple actually.
Code: Select all
DeclareModule globalVars
myVersionNumber.f = 1.23
EndDeclareModule
Module globalVars
;dummy module used purely as a container
;to relay data between parent & modules
EndModule
DeclareModule myModule
Declare displayVersionNumber()
EndDeclareModule
Module myModule
Procedure displayVersionNumber()
Debug StrF(globalVars::myVersionNumber, 2)
EndProcedure
EndModule
myModule::displayVersionNumber()
Your feedback and comments are always welcome.