This article explores Plang, an intent-based programming language designed to interpret natural language. For more information, visit plang.is or take your first steps
In a previous article on self-correcting software, I demonstrated how Plang code can self-correct. It was a simple proof of concept, but let’s dive deeper into how this works.
How Plang Works
Plang compiles into JSON files—essentially instructions—that are saved into a .pr
file. This .pr
file tells the Plang runtime how to execute your code. Keep this .pr
file in mind as it plays a crucial role in what follows.
Example of a Basic Instruction:
- read file.txt to %content%
This would generate a JSON instruction like so:
{ "ModuleType":"FileModule", "FunctionName": "ReadTextFile", "Parameters": [{"path": "file.txt"}]
}
The Plang runtime will load the appropriate FileModule, and if you examine the Program.cs file, you’ll find a method called ReadTextFile
.
This is where Plang is currently at, in version 0.1.
The Challenge: Refactoring and Compatibility
Now, imagine we want to refactor the method name from ReadTextFile
to ReadContentOfTextFile
in the Program.cs.
Any Plang code that uses the old method name(ReadTextFile
) will break and throw an error like this:
Error: Message: Method 'ReadTextFile' could not be found. 'ReadTextFile' is defined in your instruction file Key: MissingMethod
In the previous article, I showed how Plang can bind events to goals (functions) or steps (lines of code). We’ll use this feature to handle method refactoring gracefully.
Self-Correcting Code in Action
We start by creating an event listener that catches this specific error:
Events
- on any error where key="MissingMethod", call FixCode
Step 1: Collect Available Methods
First create FixCode.goal
and give it the title
FixCode
Next, we want to resolve the MissingMethod
error by finding the updated method in the FileModule
.
We need to gather all available methods in FileModule
. We can access what module caused the error from the %!step.ModuleType%
property:
- get all available methods in %!step.ModuleType%, write to %methods%
ProTip: In Plang, the
%!step%
variable always contains the current step running, including itsModuleType
, which points to theFileModule
. If you want to know what properties are in%!step%
, just write it out- write out %!step%
This will include methods like ReadContentOfTextFile
. Here’s an example of what it might look like:
[ { "Method": "public async Task<string> ReadContentOfTextFile(string path, string returnValueIfFileNotExisting = '', bool throwErrorOnNotFound = false, bool loadVariables = false, bool emptyVariableIfNotFound = false, string encoding = 'utf-8')", "Description": "Read the content from a file. Formerly ReadTextFile" }, { "Method": "public async Task WriteToFile(string path, string content, bool overwrite = false, bool loadVariables = false, bool emptyVariableIfNotFound = false, string encoding = 'utf-8')", "Description": "Write content to a file" } ... ... ...
]
Step 2: Read the Failing Instruction
Next, we read the instruction .pr
file for the step that triggered the error:
- read %!step.AbsolutePrPath%, into %instructions%
AbsolutePrPath
is a property of the %!step%
variable that points to the location of the .pr
file on your hard drive
Step 3: Call the LLM to Fix the Code
We can now pass the problem to a language model (LLM) to update the method name:
- [llm] system: The user is attempting to execute code, but the method was not found, possibly due to renaming. These are the instructions to call the C# code: %instructions% These are the available methods: %methods% Rewrite the instructions user: %!error.Message% scheme: {instructions:string}
With this input, the LLM can generate the corrected code and return it in the %instructions%
variable.
Step 4: Write the Corrected Code
Finally, we overwrite the .pr
file with the corrected code and re-execute the step:
- write %instructions% to %!step.AbsolutePrPath%
- retry step
Final Solution:
The complete self-correcting code looks like this:
FixCode
- get all available methods in %!error.ModuleType%, write to %methods%
- read %!step.AbsolutePrPath%, into %instructions%
- [llm] system: The user is attempting to execute code, but the method was not found, possibly due to renaming. These are the instructions to call the C# code: %instructions% These are the available methods: %methods% Rewrite the instructions user: %!error.Message% scheme: {instructions:string}
- write %instructions% to %!step.AbsolutePrPath%
- retry step
In-Theory Article
This is still theoretical—these modules aren’t yet available in Plang, and some features like retry step
and get all available methods
aren’t implemented. But once the language matures, this will work.
So why haven’t I implemented this yet? Well, Plang is evolving fast, and there are a thousand things to work on. But I wanted to outline this approach first.
Plang’s Upgrade Path
This opens up a fascinating upgrade path for Plang. Since Plang is still in its early stages, breaking changes are common. This approach would allow upgrades to happen almost automatically, minimizing the need for manual fixes.
Of course, there’s still a lot of engineering ahead, but the conceptual problem is solved.
Extending Plang with Modules
It’s easy to extend Plang using modules. For example, to interact with Google Drive, you could install the Google.Drive module:
Start
- get list of all my files in "tAff2u44jU2nZgpJkbY5mSwW3" on Google Drive, %files%
- write out "List of all files: %files%"
- go through %files%, call ProcessFile ProcessFile
- download file from Google Drive, fileId=%item.id%, save it to file/%item.name%
Now imagine the module developer updates the API and renames methods. Thanks to our self-correcting system, your code would still work without any changes on your end.
A Word of Caution
As programmers, we’re naturally skeptical about automated fixes happening without confirmation. This is where unit tests come into play. But that’s a topic for another article.
More Information
Interested in learning more about Plang? Here are some useful resources to get started: