> ## Content Index
> Fetch the complete content index at: https://dsl-consultancy.ghost.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# Visualizing the reduction
- URL: https://dsl-consultancy.ghost.io/visualizing-the-reduction/
- Published: 2026-10-02T06:36:58.000Z
- Updated: 2026-10-02T06:36:58.000Z
- Author: Meinte Boersma

💡

This is post 09 in my blog series on MyFPL — see [the index/table of contents](https://codeberg.org/dslmeinte/my-fpl/src/branch/main/blogs/index.adoc?ref=dsl-consultancy.ghost.io).  
  
The code in this and previous blog posts can also be found [here, on Codeberg](https://codeberg.org/dslmeinte/my-fpl?ref=dsl-consultancy.ghost.io). Check out the repo at the commit marked “blog 09”.

In the previous blog post, we implemented a visualization of the AST: the **projection** in *projectional editing*. We also adorned that visualization with the reduction of each value in the program, by explicitly calling the reducer. That doesn’t look like proper separation of concerns, so we’ll change that in this blog post.

Instead of explicitly calling the reducer at every spot where we might need the reduction of a value, we’ll compute the reduction ahead of time. Then, we store the result on a node in the AST, so the projection can just retrieve it from the AST, when and where it needs it.

At the moment, this would make virtually no difference performance-wise: we generate the HTML for our example program just once, so we also compute the reductions just the once. However, we’re also going to use the same principle to compute types of values and check constraints on AST nodes, and store the outcomes on the nodes from which they originate. Moreover, constraint checking *and* reduction benefit from already knowing the types of children of the AST node we’re dealing with. Finally, once we get to make editing a MyFPL program *interactive* (by making the projection editable), we would definitely benefit from any optimization to computing types and reductions, and checking constraints. But for now, we’ll use the simplest approach that works.

## Expanding the structure with an annotation

So, what would be a good way of storing reduced values on the AST, given that those reduced values are:

- Completely derived from that same AST, and might change as soon as the original AST changes.
- ASTs themselves.

One way of doing that, would be to extend the types of the nodes in the AST, so that it has a “slot” in which you can those reduced values. Because we’re using LionWeb, that would mean that we’d have to create an additional `reduction` containment on a suitable classifier – so, on an interface or a concept – to hold such a derived AST. In our case, the `Value` interface would seem to be suitable. Because the original AST has to be created before the reducer can do its job, that `reduction` containment would have to have optional cardinality. All in all, this would make our implementation “ugly” in the sense that it mixes concerns – rather than separating them – and forces us to deal with unset values of the `reduction` containment.

A better solution is: use *annotations*. LionWeb’s annotations are a lightweight mechanism to augment nodes in the AST with information, without having to change any classifier in MyFPL’s structure that might be a node’s type. We just have to define a meta-type for the annotation itself — let’s call it `Reduction`.

![](https://storage.ghost.io/c/eb/4d/eb4df6b9-0e4f-4d29-b975-4075848c0001/content/images/2026/09/reduction-meta-level.png)

The `Reduction` annotation annotates and also contains `Value`

A `Reduction` annotation annotates a `Value` node, and has a `result` containment: the result of reducing the annotated `Value` node. In ~~other words~~ a figure:

![](https://storage.ghost.io/c/eb/4d/eb4df6b9-0e4f-4d29-b975-4075848c0001/content/images/2026/09/reduction-node-level-2.png)

A node of `Reduction` – as an annotation of a reduced `Value` node – holds the reduction’s result `Value` node

We expand MyFPL’s structure accordingly by appending the following code to the `packages/build/structure.ts` file:

```
const Reduction = factory.annotation("Reduction").annotating(Value) ❶
factory.containment(Reduction, "result").ofType(Value) ❷
```

**Defining the* `Reduction` **annotation (in the* `packages/build/structure.ts` **file)*

1. Create an *annotation* named `Reduction`, and declare that it annotates nodes of `Value`.
2. Create a (required) containment on `Reduction` with the reduction’s result, again of type `Value`.

Running the `generate` NPM task of the `build` package produces a TypeScript class for the `Reduction` annotation.

## Adding annotation nodes to the AST

Now that we have a `Reduction` class to instantiate, we can create a function that adds the reduction for each value in a `Program`:

```
export const addReductionsTo = (program: Program) => {
    for (const value of program.values) {
        const reductionAnno = Reduction.create(nanoid()) ❶
        reductionAnno.result = reduced(value) ❷
        value.addAnnotation(reductionAnno) ❸
    }
}
```

**Adding reductions to a* `Program` **AST (in the* `packages/my-fpl/reducer.ts` **file)*

1. Create a `Reduction` node. (I happen to like the postfix “`Anno`” as abbreviation.)
2. Call the reducer with the current `value`, and set the annotation’s `result` containment to the reduced value.
3. Add the reduction annotation to the `value` node.

Now we just have to call this function with a program — e.g., our example program. We do that by adding the following line of code to the `packages/my-fpl/example.ts` file, before the invocation of the `writeFileSync` function in that file:

```
addReductionsTo(myProgram)
```

Because we *add* the reductions of all (top-level) values of a `Program` to those `Value` nodes, we should call `addReductionsTo` **only once**!

## Retrieving and visualizing the annotation

Next, we have to change the implementation of the `Projection` function, so that it doesn’t call the `reduced` reducer function itself on a `Value` AST node, but retrieves the reduced value from that node. Retrieval can be implemented as follows:

```
const reductionOn = (value: Value) =>
    value.annotations.find((anno) => anno instanceof Reduction)?.result
```

**Retrieve a reduction from an AST node (in the* `packages/my-fpl/reducer.ts` **file)*

This function either returns the first – and presumably *only* – `Reduction` annotation node, or `undefined`. That means that only the first `Reduction` annotation node added to the `node` gets returned. Append the code from the listing above to the `packages/my-fpl/reducer.ts` file.

Finally, replace the following code in that same function and file:

```
<div className="value">
    <Projection node={reduced(value)} />
</div>

```

with the following code:

```
{reductionOn(value) && ❶
    <div className="value">
        <Projection node={reductionOn(value)!} /> ❷
    </div>}

```

1. This is a bit of “standard React idiom”. React doesn’t render `undefined` values (as returned from `{…}` expressions within JSX syntax), and also doesn’t complain — not by throwing an `Error`, nor by writing something to the JS console. JavaScript’s `&&` and-operator “short-circuits” on `undefined` (and more generally: [“falsy”](https://developer.mozilla.org/en-US/docs/Glossary/Falsy?ref=dsl-consultancy.ghost.io)) values: evaluating `undefined && <RHS>` just yields `undefined` without evaluating `<RHS>`. On the other hand: `<object> && <RHS>` yields `<RHS>` because `<object>` is [“truthy”](https://developer.mozilla.org/en-US/docs/Glossary/Truthy?ref=dsl-consultancy.ghost.io). So, `{<LHS> && <JSX content>}` renders `<JSX content>` when `<LHS>` is truthy, and nothing when `<LHS>` is falsy.Also make sure to import the `reductionOn` function from `reducer.ts` in `projection.tsx`.
2. The return type of the `reductionOn` function is `Value | undefined`, but at this point in the code we can assume that `reductionOn(value)` is not `undefined`, because of the short-circuiting behavior of `&&`. So, the use of TypeScript’s non-null assertion operator `!` is warranted here.

💡

In this blog post, we computed the reductions of the values of a program, and stored that on those values as nodes of an annotation. That way, the projection can just retrieve the reduction from a node in the AST, instead of having to call the reducer itself. In the next blog, we’re going to start implementing a **type system*. That implementation will use the same approach: derive some information from the AST, and store it on the AST as (nodes of) annotations.

© 2026 Meinte Boersma (DSL Consultancy)