(Updated Wed 2026-09-02)

Modularity in Fexl

In Fexl, modularity is the ability to share common code across multiple program files. This allows you to define functions in one file and use them in several other files without having to copy the definitions everywhere. You can start writing code in a single file, and refactor it into separate files as needed.

To illustrate how this works, I'll start with an example. Let's say you're writing some code related to the old Flintstones cartoon. You can start by putting everything in one file:

try.fxl:

\\fred=(say "I am Fred.")
\\wilma=(say "I am Wilma.")
\\barney=(say "I am Barney.")
\\betty=(say "I am Betty.")

say "Meet the Flintstones."
fred
wilma
barney
betty

(Note that you define the functions with \\ instead of \ because they have side effects and you don't want them evaluated immediately at the point of definition.)

If you run that program, you see:

$ fexl try.fxl
Meet the Flintstones.
I am Fred.
I am Wilma.
I am Barney.
I am Betty.

If that's all you need to do, you're done. Just leave it alone.

Moving code to separate files

Now you would like to move those definitions into a separate file. First you create a file called "flintstones.fxl" and define all the functions like this:

flintstones.fxl:

\\fred=(say "I am Fred.")
\\wilma=(say "I am Wilma.")
\\barney=(say "I am Barney.")
\\betty=(say "I am Betty.")

define "fred" fred
define "wilma" wilma
define "barney" barney
define "betty" betty

Then change your main program to this:

try.fxl:

use "flintstones.fxl"
evaluate \;
say "Meet the Flintstones."
fred
wilma
barney
betty

Multiple libraries

You can use as many libraries as you wish, for example:

use "lib1.fxl"
use "lib2.fxl"
use "lib3.fxl"
evaluate \;
# ... your code here

Each successive library may define (or redefine) symbols in the std context.

Guarding a context

Sometimes you'll want to make some new definitions temporarily, then revert to the previous context when you're done. For that you use the guard function. It saves the current std context, runs your code in a copy of std, and then restores std to the saved version. That way any definitions in the code only have a temporary effect, and are discarded after the code runs.

try.fxl:

use "flintstones.fxl"
evaluate \;

(
guard;
define "fred" (put "Hello, " fred)
define "dino" (say "I am Dino.")
evaluate \;

fred
dino
)

evaluate \;

nl
fred # original fred
wilma
barney
betty
#dino # not defined here

Now run the program:

$ fexl try.fxl
Hello, I am Fred.
I am Dino.

I am Fred.
I am Wilma.
I am Barney.
I am Betty.

Restricting a context

Sometimes you'll want to run some code in a highly restricted context. This can be for security reasons, for example if you don't want to the code to do dangerous operations such as modifying the file system. It can also be for the purposes of isolating the code to a very specific domain-specific vocabulary of symbols.

For example, let's restrict the Flintstones to only the Fred and Wilma family.

try.fxl:

use "flintstones.fxl"
evaluate \;

restrict;

# Only allow fred and wilma.
define "fred" fred
define "wilma" wilma

evaluate \;

fred
wilma
#barney # not defined here
#betty # not defined here

Now run the program:

$ fexl try.fxl
I am Fred.
I am Wilma.

Using a temporary context

Both guard and restrict are defined in terms of the more general function use_context:

\use_context=
    (\\cx\\body
    \save=std
    set_std cx
    \x=body
    set_std save
    x
    )

The cx argument is the context, a record which defines the specific symbols you want to use. The body argument is the code you want to run within that context.

When you call use_context cx body, it first saves the current context (std). Then it sets the current context to the one you gave it and evaluates the body. Finally it restores the current context to the saved one and returns the result.

The guard function uses a copy of the current context. That way any calls to define which occur while evaluating body will affect only the copy, not the original std context.

\guard=(use_context (record_copy std))
The restrict function uses an empty context. That way the body runs with no predefined symbols whatsoever.
\restrict=(use_context empty)