3. Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025 -- Modules

main page

top: | Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025

Modules

Chapters

A modules is a compiler and interpreter entity. Modules have been first designed for the compiler that compiles modules and then, links them against libraries in order to produce executables. A module may be split into several files but a file cannot contain more than one module. A module is made of a module clause that is a list for which the `car` is the symbol `module` and followed by any Bigloo expression (that is definitions or expressions). The module clause names the module and defines the scope of the definitions. At last, the module clause is also the place where foreign bindings are defined and where classes are defined. Recent versions of Bigloo (since 2.7b) fully supports modules from the interpreter.

### 3.1 Program Structure

A Bigloo program is composed of one or more Bigloo modules where a module is defined by the following grammar:

    <module>             ==> <module-declaration> <module-body>
    <module-declaration> ==> the module declaration
    <module-body>        ==> the module body

A module is not related to a specific file and can be spread over several files if that is convenient. In particular, there is no relationship between module names and file names. The module declaration (see Module Declaration) must be the first expression in the first of the files containing the module; other expressions form the body of the module. The module body (see Core Language) contains global variables, function definitions and top level expressions (see Expressions).

### 3.2 Module declaration

The module declaration form is

module name clause ... | bigloo syntax

This form defines a module and must be the first in the file. The argument `name` is a symbol naming the module. If the same module name is used more than once, Bigloo signals an error. The runtime library is composed of modules that are read when a user module is compiled and hence, if a user module has the same name as one of the library modules, an error is signaled.
A simple module can be:

    (module foo)

    (display "this is a module")

The first line here is the complete module definition, the last line is the complete module body and together they form a complete Bigloo program. If these lines were stored in file `zz.scm`, invoking `bigloo zz.scm` would create the executable `a.out` which, when obeyed, would display `this is a module` on the terminal.
Note: Some special identifiers are reserved and can't be used to name modules. If such an identifier is used, the compiler will produce the message:

    #(module t
    #^
    # *** ERROR:bigloo:TOP-LEVEL:Parse error
    # Illegal module name -- (MODULE eval ...

The list of reserved identifiers may be enlarged for next release. For the current release that list is made of: `eval`, `foreign` and `t`.

Module `clause`s can be:

main name | bigloo module clause

This clause defines the entry point for a stand alone application to be procedure `name` of arity one. Bigloo invokes this procedure at the beginning of execution providing the list, composed of the shell command line arguments, as its single argument.

    (module foo
       (main start))

    (define (start argv)
       (display argv)
       (newline))

Then if this program is compiled into `foo` and invoked using the command `foo -t bar`, the list which is the argument for the main procedure `start` would be `("foo" "-t" "bar")`.
The special form `args-parse` helps main function argument parsing (see Command Line Parsing).

include file-name ... | bigloo module clause

This is a list of `file-name`s to be included in the source file. Include files are not modules and may have a special syntax. Thus, besides containing Bigloo expressions, they can contain import and include clauses, which must be written in a single list whose first element is the keyword `directives`. Includes files can be used to include implementation-neutral Scheme expressions and definitions in a Bigloo module. Here is an example of an include file.

    ;; `foo.sch'
    (define-struct point x y)

and the module that includes the `foo.sch' file:

    ;; `foo.scm'
    (module foo
       (include "foo.sch"))

    (print (point 1 2))

Include files, may contain module information. This is the role of the include `directives` clause here illustrated with the `bar.sch' example:

    ;; `bar.sch'
    ;; the directives
    (directives (include "foobar.sch")
                (import  hux))

    ;; expressions
    (define (gee x) (print x))

import import ... | bigloo module clause

An `import` is a list of the form:

    <import>      ==> <iclause> ...
    <iclause>     ==> (<bind-name> ... <bind-name> <module-name> <file-name> ...)
                    | (<bind-name> ... <bind-name> <module-name>)
                    | <module-name>
                    | (<module-name> <file-name> ...)
    <bind-name>   ==> <r5rs-ident>
                    | <alias-name>
    <alias-name>  ==> (<r5rs-ident> <r5rs-ident>)
    <module-name> ==> <r5rs-ident>
    <file-name>   ==> <string>

The first alternative in `iclause` imports the variable named `bind-name` which is defined in the module `module-name`, located in the files `file-name` .... The second does the same but without specifying the name of the file where the module is located. The third and the fourth form import all the exported variables of the module `module-name`.
Note: The need for specifying in which files modules are located comes from the fact that there is no automatic mapping between module names and files names. Such a mapping can be defined in a ``module access file'' (see Module Access File) or in the import clause itself, as in the first and fourth alternatives in `iclause` above.
Here is an example of an import clause:

    (module foo
       (import
          ;; import all bar exported bindings:
          bar
          ;; import the hux binding exported by
          ;; the module hux:
          (hux hux)
          ;; import the fun1, fun2 and fun3 bindings exported by
          ;; the module mod:
          (fun1 fun2 fun3 mod)
          ;; import the fun4 bindings that will be known in this module
          ;; under the alias name f
          ((f fun4) mod)
          ;; import all gee bindings. the gee module
          ;; is located in a file called `gee.scm':
          (gee "gee.scm")))

use use ... | bigloo module clause

`use` has the same meaning as `import` except that modules which are used are not initialized (see Module Initialization). Used modules are read before imported modules.

with with ... | bigloo module clause

This clause specifies a list of modules which have to be initialized at runtime and is used to force the initialization of modules which are never imported but which are required by an application (see Embedded Bigloo applications).

export export ... | bigloo module clause

In order to make a module's global bindings available to other modules, they have to be exported. Export clauses are in charge of this task and an `export` is a list of the form:

    <export>      ==> <eclause> ...
    <eclause>     ==> <varexport>
                    | <funexport>
                    | <macroexport>
                    | <classexport>
    <varexport>   ==> <ident>
    <funexport>   ==> | (<ident> <ident> ...)
                    | (inline <ident> <ident> ...)
                    | (generic <ident> <ident> <ident> ...)
    <classexport> ==> <class>
    <macroexport> ==> (macro <ident> <ident> ...)
                    | (expander <ident>)
                    | (syntax <ident>)

Note: Only bindings defined in module `m` can be exported by `m` (i.e. bindings imported by `m` cannot be exported by `m`).
The first form of `varexport` allows the variable `ident` be exported. Exported variables are mutable. That is, modules importing a variable can change its value.
The form `funexport` exports functions. An exported function is read-only. No module can modify its value. The prototype of exported functions must be explicitly specified.
Type information, specified in any `ident` in an export clause, is used by Bigloo. Where no type information is given, a default generic type named `obj` is used.
Note: The last formal argument of a multiple arity function can not be typed because this argument is bound to be a `pair` or `null`. This union cannot be denoted by any type.
Inline functions (see Inline Procedures) prototypes are prefixed by the `inline` keyword. Pay attention that in order to export an inline function, all the variables used in that function body must be exported too. It is an error to export a non-inline function using the `inline` keyword. Example:

    (module mod-exp
       (export
          ;; export the bar mutable variable
          bar
          ;; export the hux function. this
          ;; function takes exactly two arguments
          (hux x y)
          ;; export the inline function gee
          ;; that takes at least one argument.
          (inline gee x . z)))

Generic functions (see Generic functions) are exported by using the `generic` keyword. Note that methods are not exported. Only the generic function they are attached to are. It is an error to export a non-generic function using the `inline` keyword. Example:

    (module mod-exp-object
       (export
          ;; define tow classes an export them
          (class point2d x y)
          (class point3d::point2d z)
          ;; export a generic function
          (generic show ::point2d ::output-port)))

It is an error to export an inline or a generic function without using the proper keyword.
The last form `macroexport` enables macro and expanders to be exported. The prototype of the macro should be specified on the export clause. Example:

    (module mod-exp-macros
       (export
          ;; exports the ``add'' macro
          (macro add x y)
          ;; exports the ``+'' expander
          (expander +)))

    (define-macro (add x y)
       (if (and (number? x) (number? y))
           (+ x y)
           `(+ ,x ,y)))

    (define-expander +
       (lambda (x e)
          (match-case x
             ((+ ?n ?m) (+ n m))
             (else (map (lambda (z e) (e z e)) x)))))

static static ... | bigloo module clause

A `static` clause has exactly the same syntax as an export clause. However, bindings declared static are local to the module. Since the default scope of all bindings is static, `static` module clauses are useful only for program documentation.

from from ... | bigloo module clause

`from` clauses have the syntax of `import` clauses. The allow the re-exportation of imported bindings. That is, any module can export any bindings imported via a `from` clause.
As an example, suppose we have module `bar`:

    (module bar
       (export (fun)))

    (define (fun) "bar")

Now, suppose we have a module `foo` that imports `bar`, by the means of a `from` clause. Module `foo` is able to re-export the `bar` binding of module `bar`:

    (module foo
       (from (fun bar "bar.scm")))

A third module, let's name it `gee`, importing module `foo`, can see the binding for function `bar`:

    (module gee
       (import (foo "foo.scm")))

    (print (fun))

This feature is very useful when compiling modules exporting functions with type annotations. In particular, one may write:

    (module foo
      (export (class c1 x)))

Then,

    (module bar
      (import foo)
      (from foo)
      (export (fun::c1)))

    (define (fun)
       (instantiate::c1 (x 10)))

And,

    (module gee
       (import bar)
       (main main))

    (define (main x)
       (let ((o (fun)))
          (print o)
          (print (c1? o))))

load load ... | bigloo module clause

A `load` is a list of the form:

    <load>    ==> <lclause> ...
    <lclause> ==> (<module-name> <file-name>)
                | <module-name>

This clause forces Bigloo to load the module specified in the `lclause` in the environment used by the macro expansion mechanism. This means that the user's macros can use all the bindings of all the `load`ed modules but the `load`ed bindings remains unknown to the compiler.
If the module `foo` is defined by:

    (module foo
       (export (foo x)))

    (define (foo x)
       `(cons ,x ,x))

then,

    (module gee
       (load (foo "foo.scm")))

    (define-macro (gee x)
       `(cons ,(-fx x 1) ,(foo x)))

    (gee 5)   ==> (cons 4 (cons 5 5))
              => (4 5 . 5)

eval eval... | bigloo module clause

This form allows interactions between compiled code and interpreted code. (See the Section Eval command line options for a presentation of compilation flags that enable compilation tuning for `eval`.) Each `eval` has the following syntax:

    <eval> ==> (export-all)
             | (export-module)
             | (export-exports)
             | (export <bind-name>)
             | (export (@ <bind-name> <module-name>))
             | (import <bind-name>)
             | (class <bind-name>)
             | (library lib1 ...)

The first clause, `(export-all)`, exports all the variables bound in the module (i.e., the variables defined in the module and the imported variables). The second clause, `(export-module)`, exports the module to eval to so that it can be imported by other evaluated modules; the third exports all the exports (i.e. the ones present inside an `export` clause) variables to the interpreter; the fourth and fifth clause each export one variable to the interpreter. The last clause imports a variable from the interpreter and all such imported variables are immutable (i.e. they cannot be the first argument of a `set!` expression with the compiled code). Variables that are exported to the evaluators must be exported. If a variable is exported to the evaluators but not exported within an `export` clause, the compiler will produce an error message. The `library` clause makes the variables and functions of a library accessible from the interpreter.

    (module foo
       (export (fib x))
       (eval (export fib)
             (import bar)))

    (define (fib x) ...)
    (print bar)

The clause `(class <bind-name>)` exports a class definition to the interpreter. This makes the class constructor, the class predicate and the slots access functions available from the interpreter. The form `(instantiate::`class` ...)` and `(with-access::`class` ...)` are also available from the interpreter.

extern extern ... | bigloo module clause

Extern (aka foreign) clauses will be explained in the foreign interface (see C Interface).

java java ... | bigloo module clause

Java clauses will be explained in the Java interface (see Java Interface).

option option ... | bigloo module clause

This clause enables variables which affect compilation to be set from inside a module and since the expressions, `option` ..., are evaluated when compiling, no code is compiled for them. They are allowed to make side effects and to change the values of the global variables which describe how the compiler must compile. Usually they allow the control variables, which are described when Bigloo is invoked with the `-help2` option, to be set as in the following example:

    (module examplar
       (option (set! *debug* 3)
               (set! *verbose* 2)))

    (print 'dummy)

Whatever arguments are passed on the command line, Bigloo will compile this module in both verbose mode and debug mode.

library library ... | bigloo module clause

This clause enables libraries (see Bigloo Libraries) when compiling and linking Bigloo modules. The expressions `library` ... are symbols naming the libraries to be used.
Here is an example of a module declaration which makes use of a library named `format`:

    (module test
       (library format)
       (main    test-format)
       (import  (test2 "test2.scm")))

Using a library does not automatically binds its variables and functions to the interpreter. In order to make these available to the interpreter an explicit use of an eval `library` clause must be used.

type type ... | bigloo module clause

This forms is used to define builtin Bigloo types. It is not recommended to use it in user programs. So, it is left undocumented.

### 3.3 Module initialization

Initializing a module means evaluating, at runtime, its top level forms (global bindings are top level forms).
When a module, `module1`, imports a module, `module2`, `module2` is initialized before `module1`. Modules are initialized only once, nothing being done if a module already met during initialization is met again. Library modules are initialized before user modules and imported modules are initialized in the same order as they appear in import clauses.
Here is a first example with two modules. First the module `foo`:

    ;; module foo
    (module foo
       (main main)
       (import (bar "bar.scm")))

    (define (main argv)
       (print "argv: " argv))
    (print "foo")

Then the module `bar`

    ;; module bar
    (module bar)

    (print "bar")

These can be compiled into the executable `a.out` with:

    $ bigloo -c foo.scm
    $ bigloo -c bar.scm
    $ bigloo foo.o bar.o

Execution of `a.out` produces:

    $ a.out
       -| bar
          foo
          argv: (a.out)

The explanation is:

- module `foo` contains the program entry point so this is where initialization begins.
- because `foo` imports module `bar`, `bar` must be initialized before `foo`. This explains why the word `bar` is printed before anything else.
- module initialization for `foo` is completed before `main` is called. This explains why word `foo` is printed before `main` is entered.

Let's consider another example with 3 modules:

    ;; module1
    (module module1
       (main main)
       (import (module2 "module2.scm")))

    (define (main argv)
       (print "argv: " argv))

    (print "module1")

The second module:

    ;; module2
    (module module2
       (import (module3 "module3.scm")))

    (print "module2")

The third module:

    ;; module3
    (module module3
       (import (module1 "module1.scm")))

    (print "module3")

Compile with:

    $ bigloo module1.scm -c
    $ bigloo module2.scm -c
    $ bigloo module3.scm -c
    $ bigloo module1.o module2.o module3.o

Execution produces:

    $ a.out
       -| module3
          module2
          module1
          argv: (a.out)

The order of module initialization can be explicitly specified using `with` and `use` clauses.

### 3.4 Qualified notation

Global variables can be referenced using implicit notation or using qualified notation. Implicit notation is used when variables are referenced just by their name whereas qualified notation is used when variables are referenced by their name and the name of the module which defines them. Qualified notation has the following syntax:

    (@ <bind-name> <module-name>)

and is useful when several imported modules export a variable with the same name. Using qualified notations instead of short notation only affects compilation.
When several variables are defined under the same identifier, the compiler uses the two following rules in order to decide which variable is selected by an implicit reference: 1) the variable defined in a module has a higher precedence than all imported variables, 2) imported variables have a higher precedence than library variables.

### 3.5 Inline procedures

Bigloo allows procedures called inline and which differ from normal ones only in the type of code planted. An inline procedure is a first class object which can be manipulated in the same way as any other procedure but when Bigloo sees a reference to one, rather than generating a C function call to the function, the body of the inline procedure is open-coded. The definition of an inline is given in the following way:

define-inline (name args ...) body | bigloo syntax

define-inline (name args ... . arg) body | bigloo syntax

Apart from the initial word, this form has the same syntax as that used by `define` for procedures. Inline procedures are exportable which means that the compiler scans imported files to find the bodies of all inline procedures. Here is a small example of a module which exports an inline and a module which imports it.

    ;; the exporter module
    (module exporter
            (export (inline make-list . objs)))

    (define-inline (make-list . objs) objs)

    ;; the importer module
    (module importer
            (import exporter))

    (print (make-list 1 2 3 4 5))

Because of the open-coding of the exporter procedure, the above print statement is equivalent to:

    (print (let ((objs (list 1 2 3 4 5)))
              objs))

Any procedure can be an inline. Also any exported procedure can be an inline provided all global variables and functions it uses are also exported.
Note: Bigloo can decide to inline procedures declared with `define` but this can be achieved only with local procedures whereas procedures declared with the `define-inline` form are open-coded even through module importation.
Note: Procedures declared inline are macro expanded with the macro defined in the module where they are invoked. That is, if module `module1` declares an inline procedure `p` and module `module2` imports it, `p` may have two different macro-expansions: one for `module1` and one for `module2`.

### 3.6 Module access file

Bigloo is different from languages such as C where a module is defined by a file. For Bigloo, the module name is not necessarily the name of the file where the text of the module is written and modules can even be split across several files.
Since modules are defined independently of files, it is necessary to make a link between a module and its files and there are two ways of doing this. Choosing an import clause where the file-names are specified or creating a ``module access file''. Such a file must contain only one `list`, each element of the list being of the form:

    (module-name "file-name" ... "file-name")

Use the `-afile <file>` option to specify the ``module accessfile'' when compiling. By default Bigloo checks if a file named `.afile` exists. If it exists it is loaded as a module access file.
See The Bigloo command line.
Note: The Bigloo distribution contains a tool, `bglafile`, that can automatically build a ``module access file''. See the manpage for `bglafile` for details.

### 3.7 Reading path

Imported, included or loaded files are sought first in the current directory and then in the directories, sequentially from start to end, of the list in the `*load-path*` variable. This variable, initially set to the empty list, can be reset by the `-I` option of the compiler.

This Html page has been produced by Skribe.
Last update Mon Jan 27 07:55:49 2025.