True modules are rather new in the YaST world and it is planned that they will replace the old method of including modules completely (with exception of some rare cases perhaps). The following sections will outline the differences between these concepts.
YCP, originally planned as a functional language, always did dynamic (i.e. runtime) binding of variables. Although useful in many cases, it's quite puzzling for someone used to “imperative” languages. So you could well program the following block and get an unexpected result.
{ integer x = 42; define f() ``{ return x; } ... // lots of lines x = 55; return f(); // will return 55 because of runtime binding of x! } |
Another widely misused feature is to include global definitions. While there was no alternative as long as include was the only referencing instrument, this is certainly not a good programming practice in view of speed and memory considerations.
In contrast to included modules, true modules have some distinct properties that are shown in the list below.
Definition-time bindings
Definitions are evaluated in the sequence of the program flow.
One-time inclusion
In contrast to include the import statement includes the module only once even if there are more than one import statement in the program flow. Later imports are silently ignored.
Proprietary global namespace
The module definition implies a module declaration that determines the namespace of the module's global variable scope.
Local environment
Aside from the data located in the module's global namespace all other data defined in the module is purely local, i.e. is invisible from the outside.
Module constructor function
Each true module may have a constructor function that is automatically executed upon first import.
The following listing is a brief sample of a true module.
{ // This is a module called "Sample". // Therefore the file name MUST be Sample.ycp // The "module" statement makes the module accessible for 'import'. // module "Sample"; // This is a local declaration. // It can only be 'seen' inside the module. // integer local_var = 42; // This is a global declaration. // It can be accessed from outside with the name space identifier 'Sample::'. // global integer global_var = 27; // This is a global function. // It has access to global_var *and* local_var. // global define sample_f () ``{ return local_var + global_var; } } |
The module above can be used with the import statement. The syntax for file inclusion with import is similar to include. The interpreter automatically appends “.ycp” to the filename and searches below /usr/lib/YaST2/modules. If the filename starts with “./”, the file is loaded from the local directory. The global declarations of the module can then be accessed with the name space identifier Sample::.
![]() | Note |
---|---|
The file name must match the module declaration! Inside modules, only variable or function declarations are allowed. Stand-alone blocks or any kind of evaluation statements are forbidden. |
{ // This imports the 'Sample'-module. // import "Sample"; // The global function is called with the respective name space identifier. // integer i = Sample::sample_f(); // == 69 // No access to local module variables. // i = Sample::local_var; // ERROR, no access possible ! // No problem with global variables. // i = Sample::global_var; // == 27 Sample::global_var = 0; // This variable is writable !! return Sample::sample_f(); // == 42, since global_var is 0 } |
![]() | Note |
---|---|
The first encounter of the statement import "Sample"; triggers the loading of “Sample.ycp”. Subsequent import statements are ignored, because “Sample” is already defined. Consequently you can't replace a module during runtime ! |
If a global function with the same name as the module is defined, it is treated as a constructor. The constructor is called after the module has been loaded and evaluated for the first time. Because of this the constructor could (and should) be defined at the beginning of the module. Despite being located “on top” it can make use of the functions declared later in the file.
Module constructors are used mostly for initialization purposes, e.g. for setting local variables to proper values. However, the actions within a constructor can be arbitrarily complex.
![]() | Note |
---|---|
Constructors can't have any arguments. The result of calling a constructor from the outside is ignored. |
{ // This is a module called "Class" with a constructor function. // module "Class"; // A globally accessible variable. // global integer class_var = 42; // This is the constructor (same name as the module). // global define Class() ``{ class_var = 12345; } } { // The usage of the "Class"-module. // import "Class"; return Class::class_var; // will be 12345 ! } |