Welcome to the final chapter of our series! In the previous chapter, Primitive Tools Registry, we created a central "Parts Department" (Registry) that holds all the tools our AI needs, like FileReadTool and BashTool.
However, we ended that chapter with a mystery. We noticed the code used a strange symbol (??=) and wrapped the list in a function instead of just making a simple list.
This chapter explains why. We are going to learn about the Lazy Initialization Pattern.
To understand this pattern, we have to understand a specific computer error called a Circular Dependency.
Imagine two friends, Alice and Bob, trying to introduce each other at a party:
If they both wait for the other to start, nobody ever talks. The party freezes.
In our project, we have a similar situation:
If we just wrote const tools = [FileReadTool], the computer tries to load both files at the exact same millisecond. They get stuck waiting for each other, and the application crashes with an error saying "Cannot access before initialization."
Lazy Initialization solves this by saying: "Don't try to load these tools when the application starts. Wait until the very last second when the user actually asks for them."
There are two main concepts to understand here.
Being lazy is good, but we don't want to be inefficient.
We implement this pattern by moving our list inside a Function.
If we do this, the code runs immediately when the file loads, potentially causing a crash.
// BAD: Runs immediately at startup
import { FileReadTool } from './FileReadTool';
export const ALL_TOOLS = [ FileReadTool ];
We wrap it in a function. The code inside the function does not run until someone calls getTools().
// GOOD: Runs only when requested
import { FileReadTool } from './FileReadTool';
export function getTools() {
return [ FileReadTool ];
}
Let's look at how the application asks for tools using this pattern.
Let's look at the actual code in primitiveTools.ts. It is designed to be concise using modern JavaScript features.
First, we create a variable outside the function to act as our "Memory Box".
import type { Tool } from '../../Tool.js'
// ... imports ...
// This is our cache.
// Initially, it is 'undefined' (empty).
let _primitiveTools: readonly Tool[] | undefined
Next, we export the function that the rest of the app will use.
export function getReplPrimitiveTools(): readonly Tool[] {
// Use the "Nullish Coalescing Assignment" operator (??=)
return (_primitiveTools ??= [
FileReadTool,
FileWriteTool,
// ... other tools
BashTool,
])
}
??=
The magic symbol ??= is a shortcut for the logic we diagrammed above.
_primitiveTools ??= [ ... ]
This single line says:
"If
_primitiveToolsis empty, create the array and save it.
If
_primitiveToolsalready has a value, ignore the array on the right and use the saved value."
By using this function:
primitiveTools.ts, but it skips over the code inside the function.getReplPrimitiveTools().Congratulations! You have completed the REPLTool tutorial series.
In this chapter, you learned about the Lazy Initialization Pattern. We used it to make our Primitive Tools Registry safer and more stable.
Let's recap your journey:
You now have a complete understanding of how this complex AI tool manages its environment and capabilities!
Generated by Code IQ