Page 1 of 2

PureBasic Modules: A Quick Tutorial

Posted: Thu May 10, 2018 6:19 pm
by TI-994A
A QUICK & SIMPLE TUTORIAL ON THE USE OF MODULES

1. THE BASICS
The PureBasic manual defines modules as follows:
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'.
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.

Let's demonstrate this with some simple examples. Consider this empty procedure:

Code: Select all

Procedure myProcedure()  
EndProcedure
The module equivalent of the above procedure would be these set of closures:

Code: Select all

DeclareModule myModule  
EndDeclareModule

Module myModule    
EndModule
At this point, they both do absolutely nothing. So, let's add a variable to the procedure:

Code: Select all

Procedure myProcedure()
  Protected myVar = 123
EndProcedure
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:

Code: Select all

Procedure myProcedure()
  Protected myVar = 123
EndProcedure

Debug myVar   ; == 0
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:

Code: Select all

Procedure myProcedure()
  Protected myVar = 123
  ProcedureReturn myVar
EndProcedure

Debug myProcedure()   ; == 123
Quite simple and basic stuff.

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
And this would be the second way:

Code: Select all

DeclareModule myModule
  Define myVar = 123
EndDeclareModule

Module myModule  
EndModule
What's the difference? Before we answer that, let's take another look at the basic structure of the module:

Code: Select all

DeclareModule myModule  
EndDeclareModule

Module myModule    
EndModule
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:

Code: Select all

DeclareModule myModule
EndDeclareModule

Module myModule  
  Define myVar = 123
EndModule
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:

Code: Select all

DeclareModule myModule
EndDeclareModule

Module myModule  
  Define myVar = 123
EndModule

Debug myVar   ; == 0
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:

Code: Select all

DeclareModule myModule
EndDeclareModule

Module myModule  
  Define myVar = 123
EndModule

Debug myModule::myVar   ; == 0 ?
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:

Code: Select all

DeclareModule myModule
  Define myVar = 123
EndDeclareModule

Module myModule  
EndModule

Debug myModule::myVar   ; == 123
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:

Code: Select all

DeclareModule myModule
  Define myVar
EndDeclareModule

Module myModule  
  myVar = 123
EndModule

Debug myModule::myVar   ; == 123
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:

Code: Select all

Procedure myProcedure(a, b)    
  c = a + b
  ProcedureReturn c
EndProcedure

Debug myProcedure(1, 2)   ; == 3
The module-equivalent of the above procedure would look like this:

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
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:

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
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.

Code: Select all

myModule::moduleVariable
myModule::moduleFunction()
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:

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
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.

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
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?

Code: Select all

Global myVersionNumber = 1.23

DeclareModule myModule
  Declare displayVersionNumber()
EndDeclareModule

Module myModule  
  Procedure displayVersionNumber()    
    Debug myVersionNumber
  EndProcedure
EndModule

myModule::displayVersionNumber()   ; == 0 ?
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.

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()
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. :D

Re: PureBasic Modules: A Quick Tutorial

Posted: Thu May 10, 2018 8:07 pm
by Andre
Thank you! I like this very much :D

(something which would be good to be integrated into the 'beginners chapter' in the PB manual, but of course only if you agree... ;-))

Re: PureBasic Modules: A Quick Tutorial

Posted: Thu May 10, 2018 8:16 pm
by Paul
Very nicely done !

Re: PureBasic Modules: A Quick Tutorial

Posted: Thu May 10, 2018 10:46 pm
by Dude
Great tutorial! This thread needs to be made Sticky.

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 4:39 am
by TI-994A
Thank you very much, guys! Always glad to hear it's helpful. :D
Andre wrote:...good to be integrated into the 'beginners chapter' in the PB manual...
Hi Andre. I'm so honoured that you'd think so. It's all for the PureBasic community, so please use it in any way you'd see fit.

Thanks again. :D

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 5:33 am
by davido
Very instructive. Thank you. :D
Andre wrote:(something which would be good to be integrated into the 'beginners chapter' in the PB manual, but of course only if you agree... ;-))
+1

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 6:52 am
by walbus
Hi TI-994A

Still missing is the range definition and handling of global variables in a module and a reference to EnableExplicit, I think...

Regards Werner

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 9:31 am
by TI-994A
walbus wrote:Still missing is the range definition and handling of global variables in a module and a reference to EnableExplicit...
Globals are touched on in the INTEROPERABILITY section, but beyond that, they work pretty much the same way regardless of scope. And EnableExplicit is a compiler directive, but it too works the same way, according to scope.

What range definition are you referring to?

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 12:48 pm
by walbus
Hi
Yes, I know it myself
By the section I meant to explain that global variables declared in a module are available to all procedures in the module, but not outside the module.

Modules should be ever created with EnableExplicit, that's what I meant here
An EnableExplicit in a module is only valid for the module, not outside the module

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 1:01 pm
by Fred
Very nice tutorial :)

Re: PureBasic Modules: A Quick Tutorial

Posted: Fri May 11, 2018 3:34 pm
by TI-994A
Fred wrote:Very nice tutorial :)
Thank you, Fred; truly appreciate that. :D

Re: PureBasic Modules: A Quick Tutorial

Posted: Mon May 14, 2018 11:32 pm
by Andre
@TI-994A: As you agreed (thank you! :d) I will try to add the tutorial to the PB manual (beginner chapter) for the next PB version. Thank you! :D

If you plan any changes/additions just give us a note, and please include them in the first post...

Re: PureBasic Modules: A Quick Tutorial

Posted: Tue May 15, 2018 3:27 am
by TI-994A
Andre wrote:@TI-994A: As you agreed (thank you! :d) I will try to add the tutorial to the PB manual (beginner chapter) for the next PB version. Thank you! :D

If you plan any changes/additions just give us a note, and please include them in the first post...
Thank you, Andre. I'm truly glad. :D

I'll go over it and do the needful.

Re: PureBasic Modules: A Quick Tutorial

Posted: Thu May 17, 2018 8:29 am
by RSBasic
Nice tutorial.

Re: PureBasic Modules: A Quick Tutorial

Posted: Sat Jun 02, 2018 9:41 am
by applePi
Thank you TI-994A for this lesson about Modules.
apparently you have the spirit of a teacher . so i am sure the users eager for more lessons about tricky things such as pointers and so on over the years.
i suggest to change the title of this tutorial to "Lessons in PureBasic" and then add this as lesson 1. so in the future and over the years you can add lesson 2 and so on in the same thread, while updating the contents in the first page, just a suggestion.
the lessons, tutorials, Documentations are usually very hard to write and needs tremendous efforts.