> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lionlang.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Building a custom Lion environment

> Learn how to extend Lion's stdlib with your own values, functions, and objects, and how to organize custom modules with namespaceEntries.

Every Lion program runs inside an evaluation environment — a plain JavaScript object whose keys are the names Lion can resolve. The standard library is itself just an environment object exported as `stdlib`, which means extending it is as simple as spreading it alongside your own entries. This gives you a clean boundary between the Lion evaluator and the host application without any plugin system or registration API.

## Spreading stdlib with custom entries

Import `stdlib` from `@lionlang/core/modules`, spread it into a new object, and add whatever keys your Lion programs need:

```typescript theme={null}
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";

const env = {
  ...stdlib,
  price: 100,
  taxRate: 0.08,
  clamp: (min: number, max: number, value: number) =>
    Math.min(max, Math.max(min, value)),
};
```

Pass the environment as the second argument to `run`:

```typescript theme={null}
const result = await Effect.runPromise(
  run(["number/add", "price", ["number/multiply", "price", "taxRate"]], env)
);
// => 108

const clamped = await Effect.runPromise(run(["clamp", 0, 100, 150], env));
// => 100
```

<Tip>
  When a string appears as an expression and a matching key exists in the environment, Lion resolves it to that value. If no binding exists, the string evaluates to itself. This means `"price"` in a Lion expression becomes `100`, and `"unknown"` stays `"unknown"`.
</Tip>

## What can go in the environment

The environment can hold any JavaScript value. Lion will pass it through transparently to the `object` and `func` modules when needed:

| Value type        | Example                       | How Lion uses it                           |
| ----------------- | ----------------------------- | ------------------------------------------ |
| Number            | `price: 100`                  | Resolves as a numeric literal              |
| String            | `greeting: "hello"`           | Resolves as a string literal               |
| Boolean           | `debug: false`                | Resolves as a boolean                      |
| Object            | `user: { name: "Ada" }`       | Traversable with `object/get-path`         |
| Function          | `clamp: (min, max, v) => ...` | Callable as `["clamp", ...]`               |
| Class constructor | `Counter: Counter`            | Constructable with `object/new`            |
| Class instance    | `db: dbInstance`              | Methods callable with `object/call-method` |

## Organizing custom modules with namespaceEntries

The `namespaceEntries` helper exported from `@lionlang/core/modules` takes a namespace string and a record, and returns a new record with each key prefixed by `namespace/`. This is exactly how stdlib is assembled from its individual modules:

```typescript theme={null}
import { namespaceEntries, stdlib } from "@lionlang/core/modules";

const mathHelpers = {
  clamp: (min: number, max: number, value: number) =>
    Math.min(max, Math.max(min, value)),
  lerp: (a: number, b: number, t: number) => a + (b - a) * t,
};

const env = {
  ...stdlib,
  ...namespaceEntries("math", mathHelpers),
};
```

This adds `math/clamp` and `math/lerp` to the environment, consistent with how `number/add`, `string/concat`, and the other stdlib entries are named.

```json theme={null}
["math/clamp", 0, 100, 150]
```

Evaluates to `100`.

## Complete examples

The following programs are drawn directly from `packages/examples/index.ts`. All of them run against this shared environment:

```typescript theme={null}
const env = {
  ...stdlib,
  price: 100,
  taxRate: 0.08,
  clamp: (min: number, max: number, value: number) =>
    Math.min(max, Math.max(min, value)),
  user: {
    name: "Ada",
    stats: {
      score: 92,
    },
  },
};
```

### Arithmetic

References to environment variables resolve to their values before the arithmetic operation runs:

```json theme={null}
["number/add", "price", ["number/multiply", "price", "taxRate"]]
```

Returns `108` — equivalent to `100 + 100 * 0.08`.

### Conditionals

`object/get-path` traverses the nested `user` object, and `cond` branches on the result:

```json theme={null}
[
  "cond",
  [["number/greaterThan", ["object/get-path", "user", "stats.score"], 90], "great"],
  [["number/greaterThan", ["object/get-path", "user", "stats.score"], 70], "pass"],
  ["else", "retry"]
]
```

Returns `"great"` because `user.stats.score` is `92`.

### Structural match

`match` compares a value against structural predicates. `func/partial` produces a single-argument predicate by pre-filling `string/equals?`:

```json theme={null}
[
  "match",
  ["quote", { "type": "user", "profile": { "name": "Ada" } }],
  [
    {
      "type": ["func/partial", "string/equals?", ["quote", "user"]],
      "profile": { "name": "value/string?" }
    },
    ["lambda", ["value"], ["string/concat", ["object/get-path", "value", "profile.name"], " matched"]]
  ],
  ["lambda", ["value"], "value"]
]
```

Returns `"Ada matched"`.

### Host interop

A plain JavaScript function in the environment is called like any other:

```json theme={null}
["clamp", 0, 100, 150]
```

Returns `100`.

### Object path lookup

Dot-path traversal works on any nested object in the environment:

```json theme={null}
["object/get-path", "user", "stats.score"]
```

Returns `92`.
