# Modules To create a module, use the `module` special form. All modules must start with a *module declaration* which provides the module with a name, documentation string and an exports vector. Module declarations are evaluated, so you must quote any exports. Here is a simple example of a module: (module {:module 'my-module :doc "Just for showing off modules." :exports '[foo bar]} (def foo-helper (fn [x] (+ x 1))) (def foo (fn [x] (foo-helper (foo-helper x)))) (def bar (fn [x] (foo (+ x 2))))) To import a module, use `import`: (import my-module) The content of a module is evaluated in a new scope that is identical to the scope of the module definition site. This means you you can use whatever has already been defined before the module was, and that definitions in the module don't leak outside the module. The form returns the module value, which can be passed around just like any other value. This means that we can write functions which create modules: (def make-taxes (fn [VAT] (module {:module 'taxes :doc "Calculates taxes." :exports ['add-VAT]} (def add-VAT (fn [x] (* (+ 1 VAT) x)))))) And then we could use this module with different values of `VAT`: (import (make-taxes 2/10)) ;; We also need to import `prelude/io` to be able to use `print!` (import (file-module! "prelude/io.rad") :unqualified) (print! (string-append "100 EUR + VAT is: " (show (taxes/add-VAT 100)))) By default `import` will add all the definitions of the module in fully-qualified form, which means `my-module/foo` and `my-module/bar` (but not `my-module/foo-helper`) are in scope: (print! (my-module/bar 0)) You can narrow down exactly which definitions you would like to import by including a list of atoms: (import my-module ['foo]) Which would only import `my-module/foo`. And you can also use a custom qualifier: (import my-module :as 'baz) After which we can: (print! (baz/bar 42)) So we could import our taxes module twive using different qualifiers: (def taxes-fr (make-taxes 20/100)) (def taxes-de (make-taxes 19/100)) (import taxes-de :as 'de) (import taxes-fr :as 'fr) (print! (string-append "100 EUR + VAT is " (show (de/add-VAT 100)) " EUR in Germany and " (show (fr/add-VAT 100)) " EUR in France.")) If you really want to import all the definitions without a qualification, then you can use the keyword `:unqualified` like so: (import my-module :unqualified) And then we can: (print! (foo 128)) Qualification and import lists can be combined like so: (import my-module ['bar] :as 'useful) Would only add `useful/bar` to the current scope: (print! (useful/bar 0)) When working at the REPL, the function `file-module!` is also available, which can create a module from a file. It assumes the file starts with a module declaration, and is then equivalent to wrapping the contents of the file in `(module ...)`. Modules can be passed around and manipulated because they are just dicts with some metadata, and an `:env` key containing a radicle environment.