Workbench

Calculator editor

Loading the compiler…
Output
Run compiles both tabs. A failure shows “Compile failed” in the toolbar and located errors below the editor; click an error, fix it, and Run again. Reset restores the starter source in both tabs and clears errors and output. Reference opens types, examples, and setup.
Reference

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.

FieldTypeDescription
nameStringDisplay title
descriptionStringSubtitle text
package_nameStringMust match computation package
function_nameStringEntry function to call
inputsList[(String, InputEntry)]Named input controls
outputsList[(String, OutputEntry)]Named output displays
assumptionsList[AssumptionConfig]What-if toggles (or [])
snippet_max_linesIntLine 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

ConstructorArguments
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

HelperDescription
bind_discretePolymorphic in value type; declares the input as Discrete (chartable outputs render it as a bar-chart x-axis).
bind_continuousInt-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

Currency Percent Number GraphKind

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

HelperX-axis policy
currency_output / number_output / percent_outputXAxisDisallowed (scalar only)
currency_output_chartable / number_output_chartable / percent_output_chartableCaller 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

XAxisDisallowed XAxisPinned XAxisDefault XAxisOptional

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

FieldTypeDescription
nameStringToggle label
descriptionStringHelp text
variantsList[(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

TypeNotes
IntBosatsu 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.
StringText value
BoolTrue or False
List[A]Linked list, e.g. [1, 2, 3]
(A, B)Tuple, e.g. ("key", 42)
Float64Binary 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

FunctionSignature
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_StringInt -> 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:

ActionEffect
Pause typingSends the text around the caret to the agent
Ctrl+Space / Alt+\Requests a completion immediately
TabAccepts 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

yichus_playground_get_sources yichus_playground_update_sources yichus_playground_compile yichus_playground_get_output yichus_playground_reset

Takeaway: these tools read, replace, compile, inspect, or reset the two source buffers. Their exact input schemas are in registerPlaygroundWebMcpTools.

Autocomplete Tool Names

yichus_autocomplete_wait_for_request yichus_autocomplete_suggest yichus_autocomplete_get_context yichus_autocomplete_list_editors yichus_autocomplete_dismiss

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