Quite a few changes this week on @typeonce/effect-machine ποΈ
And a few are specific for agents π
API design is bound to change if we don't write code anymore. The question becomes: "how can the API make sure agents understand and can verify correctly?" π
API for humans
When you write code, you want it to be expressive but clean, shorter and easier to read.
Having shorter API code requires hiding the complexity internally. Less control for more ergonomics π
But some information cannot be derived or hidden. You either remove them from the API, or you require the user to provide them.
Often times, removing them would have been a good enough tradeoff. This is how the effect-machine API looked like before:
export const TurnstileMachine = definition.handle({
Locked: {
on: {
CoinInserted: ({ target }) => target.full.Unlocked.from()
}
},
Unlocked: {
on: {
GatePushed: ({ target }) => target.full.Locked.from()
}
}
})Every transition would run some arbitrary code and checks in a normal function. You can use if/else to branch, and just select the next target.
Easy and readable for a human, not so good for testing and verification π€
The machine cannot statically check what's the next state from the transition. A function can implement all sort of checks, and the verifier cannot confirm the next target π
API for agents
Since then, with version v0.14.0, the same example changed to the following:
export const TurnstileMachine = definition.handle({
Locked: {
on: {
CoinInserted: Machine.transition({
target: (to) => to.full.Unlocked(),
resolve: ({ target }) => target.from()
})
}
},
Unlocked: {
on: {
GatePushed: Machine.transition({
target: (to) => to.full.Locked(),
resolve: ({ target }) => target.from()
})
}
}
})Quite a few additions: Machine.transition, target, resolve (and more).
This new API is completely explicit, and can be statically checked βοΈ
target defines the next state statically, for each possible branch (instead of if/else). resolve can run any sort of machine logic (raise, spawn etc.) but it cannot modify the target.
The API is longer, more verbose, and more explicit. Less convenient for a human, but ideal for an agent:
- Longer or more verbose code is not an issue for agents
- More explicit code encodes more information
- Static branches unlocks many more testing APIs
What agents care about
I am testing the effect-machine API manually myself to discover all usability issues. And I found quite a few already, and fixed.
But the core of the exploration is about finding common pitfalls, and prevent those in the API directly π
A few examples:
- Adding a
Machine.eventsAPI to prevent agents exporting and leaking events everywhere - Removing schema requirements when the schema is empty (no state)
- Removing multiple ways of achieving the same goal, to encourage the proper usage
Many of those use cases were justified with a human. You knew the tradeoffs, and you could choose to not misuse an API.
But if agents are writing the code, you want to prevent any sort of bad pattern at the API level π
On top of that, the MachineTest module allows to generate all sort of use cases and verify the machine never breaks.
Agent-driven API design, with an extensive automatic testing API πͺ
Alongside the agent-driven changes, the API has something for you, human, as well. More clear visuals and an easier model API.
effect-machine now supports Mermaid diagrams as well Full configuration and transitions explicit in the model ποΈ
Easy to skim and verify the code architecture, less focus on the details π
See you next π
