> ## 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.

# Calling JavaScript from Lion programs

> Learn how Lion programs interact with host JavaScript objects, classes, and functions through the object and func standard library modules.

Lion is designed to sit on top of ordinary JavaScript. Any value you place in the evaluation environment — a plain object, a class constructor, a function, or a live instance — becomes a first-class citizen inside a Lion program. The `object` and `func` standard library modules expose the full surface of host JavaScript through simple, composable JSON expressions.

## Accessing object properties

Use `object/get` to read a single named property from a host object, and `object/get-path` to traverse a nested structure with a dot-separated path. Numeric segments in a path index into arrays.

```json theme={null}
["object/get", { "a": 1 }, "a"]
```

```json theme={null}
["object/get-path", { "a": { "b": { "c": 42 } } }, "a.b.c"]
```

When a host object is available in the environment under a string key, pass the key instead of the literal object:

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

With `{ user: { name: "Ada", stats: { score: 92 } } }` in the environment, this returns `92`. The path `"stats.score"` resolves two levels deep. A path segment like `"users.1.name"` would index the second element of an array named `users`.

## Calling object methods

`object/call-method` invokes a method by name on a host object, forwarding any additional arguments:

```json theme={null}
["object/call-method", "obj", "methodName", "arg1", "arg2"]
```

For methods nested deeper in an object tree, use `object/call-method-path` with a dot path:

```json theme={null}
["object/call-method-path", "obj", "nested.methodName", "arg1"]
```

Both forms bind `this` correctly — the method is called on the object at the path before the final key, so prototype methods work as expected.

`object/get-method` and `object/get-method-path` retrieve the bound method without calling it immediately, which is handy when you want to store a method reference for later use:

```json theme={null}
["object/get-method", "obj", "methodName"]
```

## Constructing classes

`object/new` calls a constructor with `new`, forwarding all arguments:

```json theme={null}
["object/new", "SomeClass", "arg1", "arg2"]
```

`SomeClass` must be present in the environment as a function. The result is a fully constructed instance that can then be used with `object/call-method` or stored with `define`.

## Binding Lion lambdas as JavaScript callbacks

`func/callback` wraps a Lion lambda so it can be handed to any JavaScript API that expects a plain function — promise handlers, event listeners, array iterators, and so on. The wrapper captures the current Lion environment so the lambda continues to have access to all bindings:

```json theme={null}
["func/callback", ["lambda", ["x"], ["number/add", "x", 1]]]
```

When the callback is invoked by the host, it runs the lambda synchronously and returns the result, or returns a Promise if the lambda body yields an effectful value.

## Partial application

`func/partial` pre-fills the leading arguments of any callable — host functions and Lion lambdas alike:

```json theme={null}
["func/partial", "fn", 1, 2]
```

The returned function accepts the remaining arguments when called. This is particularly useful inside `match` structural predicates, where you need a single-argument predicate but only have a two-argument equality function:

```json theme={null}
["func/partial", "string/equals?", ["quote", "user"]]
```

<Note>
  `func/partial` works on any value stored in the environment under a string key, including stdlib entries such as `"string/equals?"` or `"number/greaterThan"`.
</Note>

## Real-world example: the agent package

The `packages/agent` package is the most complete demonstration of host interop. It injects into the Lion environment:

* OpenTUI UI classes and layout enums
* AI SDK streaming helpers
* A model instance
* Node `fs`
* Zod schema constructors
* A live renderer instance

The agent program in `agent.json` then orchestrates the entire terminal UI using only Lion expressions — constructing components with `object/new`, traversing nested APIs with `object/get-path`, calling methods with `object/call-method`, and attaching event handlers with `func/callback`.

## Walkthrough: injecting a class, constructing it, and calling its methods

The following steps show the full lifecycle from TypeScript to Lion.

<Steps>
  <Step title="Inject the class into the environment">
    Add the constructor as an environment entry alongside `stdlib`. Any string key works; choose a name your Lion code will reference.

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

    class Counter {
      private count: number;
      constructor(initial: number) {
        this.count = initial;
      }
      increment(by: number) {
        this.count += by;
        return this.count;
      }
      value() {
        return this.count;
      }
    }

    const env = {
      ...stdlib,
      Counter,
    };
    ```
  </Step>

  <Step title="Construct an instance with object/new">
    Inside a Lion `begin` block, use `object/new` to create an instance and `define` to bind it:

    ```json theme={null}
    [
      "begin",
      ["define", "counter", ["object/new", "Counter", 0]],
      ["object/call-method", "counter", "increment", 5],
      ["object/call-method", "counter", "value"]
    ]
    ```

    This evaluates to `5`.
  </Step>

  <Step title="Run the program from TypeScript">
    Pass the program and environment to `run`, then execute with Effect:

    ```typescript theme={null}
    const program = [
      "begin",
      ["define", "counter", ["object/new", "Counter", 0]],
      ["object/call-method", "counter", "increment", 5],
      ["object/call-method", "counter", "value"],
    ];

    const result = await Effect.runPromise(run(program, env));
    // => 5
    ```
  </Step>

  <Step title="Pass a Lion callback to a host method">
    If `Counter` had an `onChange` method that accepted a listener, you could attach a Lion lambda using `func/callback`:

    ```json theme={null}
    [
      "begin",
      ["define", "counter", ["object/new", "Counter", 0]],
      [
        "object/call-method",
        "counter",
        "onChange",
        ["func/callback", ["lambda", ["newValue"], ["console/log", "newValue"]]]
      ],
      ["object/call-method", "counter", "increment", 10]
    ]
    ```

    The lambda runs inside the Lion environment each time the host calls the listener.
  </Step>
</Steps>
