Both [import][] and [import*][] are actually shorthands and what they accomplish can be done using the lower-level builtins [def][], [use][] and [require][]. Here is how you could replace [import][]: #(with import:) (import math) (trace (math/+ 1 2)) #(with def and require:) (def math (require "math")) (trace (math/+ 1 2)) [require][] returns a *scope*, which is defined as the symbol `math`. Then `math/+` is resolved by looking for `+` in this nested scope. Note that the symbol that the scope is defined as and the name of the module that is loaded do not have to be the same, you could call the alias whatever you want: #(this not possible with import!) (def fancy-math (require "math")) (trace (fancy-math/+ 1 2)) Most of the time the name of the module makes a handy prefix already, so [import][] can be used to save a bit of typing and make the code look a bit cleaner. [import*][], on the other hand, defines every symbol from the imported module individually. It could be implemented with [use][] like this: (use (require "math")) (trace (+ 1 2)) [use][] copies all symbol definitions from the scope it is passed to the current scope. Note that [import][], [import*][], [def][], and [use][] all can take multiple arguments: #(using the shorthands:) (import* math logic) (import midi osc) #(using require, use and def:) (use (require "math") (require "logic")) (def midi (require "midi") osc (require "osc")) It is common to have an [import][] and [import*][] expression at the top of an `alv` program to load all of the modules that will be used later, but the modules don't necessarily have to be loaded at the very beginning, as long as all symbols are defined before they are being used. ## nested scopes Once a symbol is defined, it cannot be changed or removed: (def a 3) (def a 4) #(error!) It is, however, possible to 'shadow' a symbol by re-defining it in a nested scope: So far, all symbols we have defined - using `def`, [import][] and [import*][] - have been defined in the *global scope*, the scope that is active in the whole `alv` program. The [do][] builtin can be used to create a new scope and evaluate some expressions in it: (import string) (def a 1 b 2) (trace (.. "first: " a " " b)) (do (def a 3) (trace (.. "second: " a " " b)) (trace (.. "third: " a " " b)) This example prints the following: trace (.. "first: " a " " b): trace (.. "second: " a " " b): trace (.. "third: " a " " b): As you can see, within a nested scope it is possible to overwrite a definition from the parent scope. Symbols that are not explicitly redefined in a nested scope keep their values, and changes in the nested scope do not impact the parent scope.