Workbench
This playground edits
Bosatsu,
a small pure functional language; Yichus is a static-analysis and
simulation toolkit built on it (overview).
Run compiles both files in your browser, using a
Scala.js build of the Yichus compiler. Your code is not sent to a
server. (Exception: once a local agent is connected, it can read
the full sources via yichus_playground_get_sources,
and autocompletion sends the text near your caret to it. If that
agent is backed by a hosted LLM, the text leaves your machine
through the agent.) The two tabs are one
program: computation.bosatsu holds the domain logic,
and config.sim.bosatsu declares the simulation UI
(inputs and outputs) around one entry function exported by the
computation package. (In the rendered simulation, each output has a
Why? button explaining its derivation; "Suggest Change"
appears when you pick a value there to edit and re-run.)
SimConfig Fields and Entry Binding
Takeaway: the top-level configuration binding must be named
config, and its fields select the computation entry
point and simulation surface. The canonical declaration is in the
Yichus/Simulation source.
| Field | Type | Description |
|---|---|---|
| name | String | Display title |
| description | String | Subtitle text |
| package_name | String | Must match computation package |
| function_name | String | Entry function to call |
| inputs | List[(String, InputEntry)] | Named input controls |
| outputs | List[(String, OutputEntry)] | Named output displays |
| assumptions | List[AssumptionConfig] | What-if toggles (or []) |
| snippet_max_lines | Int | Line cap for the source snippet shown in a Why? panel, the derivation explanation behind each output's Why? button in the rendered simulation (0 = no snippet) |
Typed InputSpec Constructors
| Constructor | Arguments |
|---|---|
int_slider | (min, max, step, default), all Int |
int_number_input | (min, max, step, default), all Int |
int_dropdown | (options: List[(String, Int)], default: Int) |
bool_checkbox | (default: Bool) |
bool_dropdown | (options: List[(String, Bool)], default: Bool) |
string_dropdown | (options: List[(String, String)], default: String) |
Each returns an InputSpec[a]; the value type is determined by the constructor.
(Signatures verified against the shipped
Yichus/Simulation source.)
Discrete and Continuous Input Bindings
| Helper | Description |
|---|---|
bind_discrete | Polymorphic in value type; declares the input as Discrete (chartable outputs render it as a bar-chart x-axis). |
bind_continuous | Int-only; declares the input as Continuous (chartable outputs render it as a line-chart x-axis). |
Both helpers yield an InputHandle[a]. Read the typed default with value(handle). Takeaway: the binding records an input's chart discreteness as well as its typed value. See the bind helper definitions.
OutputFormat Variants
GraphKind identifies a multi-layer graph card
produced by graph_output. It is not a scalar result
field and its x-axis mode is disallowed because the graph carries
its own series and bounds. Takeaway: choose Currency, Percent, or
Number for scalar outputs; use the
graph constructors
for GraphKind.
Scalar and Chartable Output Helpers
| Helper | X-axis policy |
|---|---|
currency_output / number_output / percent_output | XAxisDisallowed (scalar only) |
currency_output_chartable / number_output_chartable / percent_output_chartable | Caller supplies an XAxisMode |
Scalar helpers take (name: String, label: String, value,
primary: Bool); the chartable variants add a final
XAxisMode. primary marks this output as
the simulation's headline result. It is carried through to
output metadata and the agent tools rather than being a
formatting switch. Takeaway: the helper fixes the output format
and whether an x-axis policy is available. The exact signatures
are in Yichus/Simulation.
XAxisMode Rendering Policies
Pinned/Default/Optional take typed InputHandle[Int] references and a ChartRange(samples, min_override, max_override).
XAxisDisallowed renders only the scalar value.
XAxisPinned always charts against one fixed input and
offers no axis selector. XAxisDefault starts as a
chart on the named default and can select from its eligible list.
XAxisOptional adds a scalar/chart toggle; a missing
default starts in scalar mode. Takeaway: the enum controls both
initial rendering and available controls. See
ChartableOutputRuntime.scala.
AssumptionConfig Parsed Fields
| Field | Type | Description |
|---|---|---|
| name | String | Toggle label |
| description | String | Help text |
| variants | List[(String, String)] | (label, function_suffix) pairs |
The extractor currently parses and retains each variant's label
and suffix. This reference does not promise that choosing a
variant automatically dispatches to a suffixed function; the
current generator has no such dispatch path. Takeaway: treat the
suffix as configuration data unless the consuming product
documents its use. See
ConfigExtractor.scala.
Compiling Two-File Simulation Example
In the computation.bosatsu tab, which holds the
domain logic, with the struct and function the config will reference:
package MyPackage from Bosatsu/Predef import mul export (Result(), calculate) struct Result(total: Int) def calculate(x: Int) -> Result: Result(x.mul(3))
In the config.sim.bosatsu tab, where it imports
calculate and Result from the
computation package:
package MyPackage/Config
from MyPackage import calculate, Result
from Yichus/Simulation import (
int_slider, SimBuilder, bind_continuous, outputs,
currency_output, sim, value
)
from Bosatsu/Predef import True
def config_builder() -> SimBuilder:
x_h <- bind_continuous("x", "X", int_slider(0, 100, 1, 50))
result <- outputs(calculate(value(x_h)))
match result:
case Result(total):
[
currency_output("total", "Total", total, True)
]
config = sim(
"My Sim", "Description",
"MyPackage", "calculate",
config_builder(),
[], 0
)
Each h <- bind_continuous(...) line is sugar for bind_continuous(..., h -> rest), so the next bind sees h in scope without nesting lambdas. The same pattern works for bind_discrete and outputs. With the example's default input, the expected result value is 150, rendered as the Total currency output. Takeaway: paste the first block into the computation tab, the second into the config tab, and click Run. The playground E2E covers in-browser compilation and output.
Simulation API Import Set
from Yichus/Simulation import ( int_slider, int_number_input, int_dropdown, bool_checkbox, bool_dropdown, string_dropdown, SimBuilder, value, bind_discrete, bind_continuous, outputs, number_output, currency_output, percent_output, number_output_chartable, currency_output_chartable, percent_output_chartable, sim )
Takeaway: import only the constructors used by your config. The complete export list is in the package source.
Bosatsu Packages and Imports
package MyPackage from Bosatsu/Predef import Int, String, Bool, List from Yichus/Num/Float64 import Float64, int_to_Float64
Each file starts with package Name. Use from Pkg import ... to bring names into scope. Takeaway: package declarations establish identity, and imports bring exported names into scope. The Bosatsu language guide is the authoritative syntax route.
Core Value Types
| Type | Notes |
|---|---|
| Int | Bosatsu defines arbitrary-precision integers. Yichus’s generated JavaScript currently uses numbers within the safe-integer range; out-of-range literals are rejected, and arithmetic must stay within that range. See the backend’s numeric contract. |
| String | Text value |
| Bool | True or False |
| List[A] | Linked list, e.g. [1, 2, 3] |
| (A, B) | Tuple, e.g. ("key", 42) |
| Float64 | Binary floating-point arithmetic from Yichus/Num/Float64. Convert integer inputs with int_to_Float64. |
Takeaway: use these types in annotations and let the compiler reject mismatches. See the language guide for type and literal syntax.
Struct Construction and Destructuring
struct Point(x: Int, y: Int) p = Point(3, 4) # Access fields via pattern matching
Takeaway: structs group typed fields and patterns read them. The language guide covers struct declarations and matches.
Function Definitions and Calls
def add(a: Int, b: Int) -> Int: a.add(b) def greet(name: String) -> String: "Hello, ".concat(name)
Functions use indentation for body. Types can be inferred; explicit parameter and return annotations document a function’s interface. Takeaway: a function body is the final expression in its indented block. See the function syntax reference.
Exhaustive Pattern Matching
match point:
case Point(x, y):
x.add(y)
Exhaustive matching is required. Use _ for wildcard. Takeaway: each constructor must be covered or the wildcard must handle the remainder. See the pattern reference.
Enum Constructors and Matches
enum Color: Red, Green, Blue match c: case Red: "red" case Green: "green" case Blue: "blue"
Constructors are bare names imported directly. Takeaway: enum values select one declared variant, and exhaustive matching handles them. See the enum reference.
Local Bindings and Method-Call Sugar
def calc(x: Int) -> Int: doubled = x.mul(2) offset = doubled.add(10) offset
Bindings are name = expr. The last expression is the return value.
x.mul(2) is method-call sugar for mul(x, 2) --
any function can be called this way on its first argument, with no automatic currying.
Takeaway: local names bind immutable expression results. See the
Bosatsu guide
for declaration blocks and application syntax.
Common Bosatsu/Predef Functions
| Function | Signature |
|---|---|
| add | (Int, Int) -> Int |
| sub | (Int, Int) -> Int |
| mul | (Int, Int) -> Int |
| div | (Int, Int) -> Int |
| mod_Int | (Int, Int) -> Int |
| eq_Int | (Int, Int) -> Bool |
| cmp_Int | (Int, Int) -> Comparison |
| int_to_String | Int -> String |
| concat_String | (String, String) -> String |
Takeaway: these functions cover common integer, comparison, and string operations. Check the upstream builtin package sources when a signature is uncertain.
Fractional arithmetic
Use these imports in a calculation that needs fractional results. The aliases let you write arithmetic with ordinary operators.
from Yichus/Num/Float64 import ( Float64, int_to_Float64, addf as `+`, subf as `-`, mulf as `*`, divf as `/` )
Read the complete numeric package for powers, comparisons, and other operations.
Immutability, Recursion, and Numeric Imports
All values are immutable. Bosatsu supports checked recursion,
including recur blocks over structurally smaller
values; recursive definitions must satisfy the totality checker.
foldl_List from Bosatsu/Predef remains
useful when a fold expresses the operation directly.
Integer literals and arithmetic use Int. Convert inputs with int_to_Float64 when your calculation requires fractions.
Full language guide: johnynek.github.io/bosatsu/language_guide.html
Local WebMCP Relay Connection
This editor publishes WebMCP tools through a compatible browser host or a local relay, so an agent (Claude Code, Claude Desktop, Cursor, or anything that speaks MCP) can read and edit the sources, compile, and power autocompletion. Native site-tool hosts can use the page directly. For an ordinary browser, these three steps connect the local relay:
1. Build the relay.
@yichus/mcp is not published to npm, so
npx will not find it. Build it from a
checkout with Node.js 20 or newer and
pnpm:
git clone https://github.com/snoble/yichus && cd yichus pnpm install pnpm run yichus-mcp:build
2. Register the relay with your MCP client. Add this to the client's MCP config (Claude Desktop, Cursor, Windsurf, Claude Code all accept stdio servers in this shape), replacing the path with the absolute path to your checkout; the client launches and keeps the relay process running:
{
"mcpServers": {
"yichus": {
"command": "node",
"args": ["/absolute/path/to/yichus/packages/yichus-mcp/dist/cli.js"]
}
}
}
(To try the relay without a client config, run that same command in a terminal. It stays in the foreground while connected.)
3. Attach this page to the relay. Reload with
?webmcp=1 in the URL (or set
localStorage.yichus_webmcp = "on"). The page then
registers its tools with the local relay; without that flag the
page never attaches to the local relay. Native site tools do not require the flag. To check the connection, ask the agent to
call yichus_autocomplete_list_editors. It lists
the editors currently attached. Takeaway: both the relay process
and the page opt-in are required. The
relay README
documents the connection boundary.
Revision-Safe Agent Autocompletion
Ask your connected agent to loop: call
yichus_autocomplete_wait_for_request (it blocks until
you pause typing or request a completion), answer with
yichus_autocomplete_suggest, repeat. With that loop
running:
| Action | Effect |
|---|---|
| Pause typing | Sends the text around the caret to the agent |
| Ctrl+Space / Alt+\ | Requests a completion immediately |
| Tab | Accepts the ghost-text suggestion |
| Escape (or keep typing) | Dismisses the suggestion |
Suggestions are pinned to the editor revision they were computed
against; if the text changes before they arrive, they are discarded
instead of applied. Takeaway: keep the wait/suggest loop running,
and expect stale completions to be rejected. The protocol and
schemas are implemented in
bosatsu-autocomplete.js.
Check the result the user can see
Read sources and their revision first. Pass expectedRevision when updating them so a newer user edit is not overwritten. Compile, then call yichus_playground_get_output until outputReady is true. Its feedback includes rendered text, controls, chart count, page width, and runtime errors. Errors and source edits clear the previous output. This feedback is not a screenshot.
Connection instructions and the shared-example workflow. For calculators embedded in guides, the example tools can also change preview controls and open Why.
Playground Editing Tool Names
Takeaway: these tools read, replace, compile, inspect, or reset
the two source buffers. Their exact input schemas are in
registerPlaygroundWebMcpTools.
Autocomplete Tool Names
Takeaway: use editing tools for whole-source compile cycles and
autocomplete tools for revision-pinned suggestions. The exact
editing-tool JSON schemas live in
registerPlaygroundWebMcpTools;
autocomplete schemas live in
bosatsu-autocomplete.js.
Full relay setup:
github.com/snoble/yichus, packages/yichus-mcp/README.md
Where this fits
What this is about: Trace a result to its inputs