Oforth Tutorial 2b : Packages and namespaces
I - Packages
Packages allow to regroup a list of files to be loaded to implement a global functionality.
A package is implemented as a file with name packageName.pkg
In order to be loaded, this file must be present into one of the directories of OFORTH_PATH environnement variable (or into a pack directory included one of them).
This file is an Oforth source that pushes 2 lists on the stack : package signature and the list of package file names.
// ****************************************************************************
// TCP package signature
// ****************************************************************************
[
[ $name, "tcp" ],
[ $version, "V0.9.0" ],
[ $state, $NotStable ],
[ $directory, "tcp" ],
[ $dependencies, [ ] ],
[ $author, "Franck Bensusan" ],
[ $contributors, [ ] ],
[ $url, "www.oforth.com" ],
[ $license, "BSD" ]
]
// ****************************************************************************
// TCP files
// ****************************************************************************
[ "TcpConnection.of", "TcpRequest.of", "TcpServer.of" ]
The most important field of a package signature is $directory. This directory is the location of package files.
For instance, if tcp.pkg file has been found into /home/oforth/pack directory, then the 3 files should be located into /home/oforth/pack/tcp
Package files loading order is the order of their declaration into the package file list.
Packages themselves are words, like functions, classes, ... So a package can't have the same name than another word.
II - Namespaces
All packages are loaded into their own namespace, in order to avoid name conflits : 1 package = 1 namespace.
At anytime, the system has a current namespace and all words are created into this package. When a package is loaded, the current namespace is set to this package name, files are loaded and definitions are created into this namespace. Then the current namespace is set back to its previous value.
Two differents words can have the same name into two different packages.
>#dup package .
oforth ok
>#apply package .
lang ok
A word can be fully qualified by prefixing its name with its package :
12 oforth:dup .s
[1] (Integer) 12
[2] (Integer) 12
>12 lang:dup
[1:interpreter] ExCompiler : Can't evaluate
#dup is declared into oforth package, not into lang package.
At Oforth startup :
1) Package oforth is created and current package is set to "oforth" package.
2) Built-in words are created into oforth package.
3) Package lang is imported into oforth package (see importing packages).
4) After lang has been loaded, system is operationnel and current package is "oforth".
Two functions are available to load a package :
use: package
import: package
III - Use a package
Using a package allows to load all package files into their own namespace. Package words must be qualified to be used, ie all words must be prefixed by the package name.
When you use a package (from oforth package or another package) :
- If the package has already been loaded into the system, do nothing and returns.
- The package word is created into oforth package.
- The current package is now set to this package.
- The package is loaded : all package files are loaded and words are created into this new namespace.
- The current package is set back to its previous value.
So, in order to use words created into this package form outside, names must be qualified :
packageName:word
It is possible to create an alias for a particular word of a used package :
#packageName:word alias: myWord
myWord is now created as an alias of packageName:word into the current package.
Example : Uses package tcp and creates an alias for a word created into tcp package :
use: tcp
ok
>TcpRequest .
[1:interpreter] ExCompiler : Can't evaluate
>tcp:TcpRequest .s
[1] (Class) TcpRequest
ok
>#tcp:TcpRequest alias: TcpRequest
ok
>TcpRequest .s
[1] (Class) TcpRequest
ok
>
IV - Import a package
All packages have a list of imported packages. This list is used when searching for a non qualified word (see search order). This allows to use words declared into an imported package as if they were declared into the current package (ie non qualified).
When you import a package :
- "use: package" is run
- If the package is not into the current package list of imported packages, it is added to the list.
Example :
import: tcp
ok
>TcpRequest .s
[1] (Class) TcpRequest
ok
>#TcpRequest package .
tcp ok
V - Search order for words
Each time a word is searched (by the interpreter, using # or using #find), the search order is :
- If the word is qualified, search only into the qualified package and returns
- If the word is non qualified :
Consequences :
1) Non qualified words are searched into imported packages of current package but not into imported packages of those packages. If needed, they must be imported too.
2) Words declared into packages imported into oforth package are available into all packages even if these word are not qualified.
3) There is no conflict error when a non qualified word is used and has a name declared more than once. The first word found using the previous search order will be used.
Franck