moonscratch 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.agents/skills/moonbit-agent-guide/LICENSE +202 -0
  2. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +1126 -0
  3. package/.agents/skills/moonbit-agent-guide/SKILL.md +1126 -0
  4. package/.agents/skills/moonbit-agent-guide/ide.md +116 -0
  5. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +106 -0
  6. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +422 -0
  7. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +422 -0
  8. package/.agents/skills/moonbit-practice/SKILL.md +258 -0
  9. package/.agents/skills/moonbit-practice/assets/ci.yaml +25 -0
  10. package/.agents/skills/moonbit-practice/reference/agents.md +1469 -0
  11. package/.agents/skills/moonbit-practice/reference/configuration.md +228 -0
  12. package/.agents/skills/moonbit-practice/reference/ffi.md +229 -0
  13. package/.agents/skills/moonbit-practice/reference/ide.md +189 -0
  14. package/.agents/skills/moonbit-practice/reference/performance.md +217 -0
  15. package/.agents/skills/moonbit-practice/reference/refactor.md +154 -0
  16. package/.agents/skills/moonbit-practice/reference/stdlib.md +351 -0
  17. package/.agents/skills/moonbit-practice/reference/testing.md +228 -0
  18. package/.agents/skills/moonbit-refactoring/LICENSE +21 -0
  19. package/.agents/skills/moonbit-refactoring/SKILL.md +323 -0
  20. package/.githooks/README.md +23 -0
  21. package/.githooks/pre-commit +3 -0
  22. package/.github/workflows/copilot-setup-steps.yml +40 -0
  23. package/.turbo/turbo-typecheck.log +2 -0
  24. package/AGENTS.md +91 -0
  25. package/LICENSE +21 -0
  26. package/PLAN.md +64 -0
  27. package/README.mbt.md +77 -0
  28. package/README.md +84 -0
  29. package/TODO.md +120 -0
  30. package/a.png +0 -0
  31. package/benchmarks/calc.bench.ts +144 -0
  32. package/benchmarks/draw.bench.ts +215 -0
  33. package/benchmarks/load.bench.ts +28 -0
  34. package/benchmarks/render.bench.ts +53 -0
  35. package/benchmarks/run.bench.ts +8 -0
  36. package/benchmarks/types.d.ts +15 -0
  37. package/docs/scratch-vm-specs/eventloop.md +103 -0
  38. package/docs/scratch-vm-specs/moonscratch-time-separation.md +50 -0
  39. package/index.html +91 -0
  40. package/js/AGENTS.md +5 -0
  41. package/js/a.ts +52 -0
  42. package/js/assets/AGENTS.md +5 -0
  43. package/js/assets/base64.test.ts +14 -0
  44. package/js/assets/base64.ts +21 -0
  45. package/js/assets/build-asset.test.ts +26 -0
  46. package/js/assets/build-asset.ts +28 -0
  47. package/js/assets/create.test.ts +142 -0
  48. package/js/assets/create.ts +122 -0
  49. package/js/assets/index.test.ts +15 -0
  50. package/js/assets/index.ts +2 -0
  51. package/js/assets/types.ts +26 -0
  52. package/js/assets/validation.test.ts +34 -0
  53. package/js/assets/validation.ts +25 -0
  54. package/js/assets.test.ts +14 -0
  55. package/js/assets.ts +1 -0
  56. package/js/index.test.ts +26 -0
  57. package/js/index.ts +3 -0
  58. package/js/render/index.test.ts +65 -0
  59. package/js/render/index.ts +13 -0
  60. package/js/render/sharp.ts +87 -0
  61. package/js/render/svg.ts +68 -0
  62. package/js/render/types.ts +35 -0
  63. package/js/render/utils.ts +108 -0
  64. package/js/render/webgl.ts +274 -0
  65. package/js/sharp-optional.d.ts +16 -0
  66. package/js/test/helpers.ts +116 -0
  67. package/js/test/hikkaku-sample.test.ts +37 -0
  68. package/js/test/rubik-components.input-motion.test.ts +60 -0
  69. package/js/test/rubik-components.lists.test.ts +49 -0
  70. package/js/test/rubik-components.operators.test.ts +104 -0
  71. package/js/test/rubik-components.pen.test.ts +112 -0
  72. package/js/test/rubik-components.procedures-loops.test.ts +72 -0
  73. package/js/test/rubik-components.variables-branches.test.ts +57 -0
  74. package/js/test/rubik-components.visibility-entry.test.ts +31 -0
  75. package/js/test/test-projects.ts +598 -0
  76. package/js/test/variable.ts +200 -0
  77. package/js/test/warp.test.ts +59 -0
  78. package/js/vm/AGENTS.md +6 -0
  79. package/js/vm/README.md +183 -0
  80. package/js/vm/bindings.test.ts +13 -0
  81. package/js/vm/bindings.ts +5 -0
  82. package/js/vm/compare-operators.test.ts +145 -0
  83. package/js/vm/constants.test.ts +11 -0
  84. package/js/vm/constants.ts +4 -0
  85. package/js/vm/effect-guards.test.ts +68 -0
  86. package/js/vm/effect-guards.ts +44 -0
  87. package/js/vm/factory.test.ts +486 -0
  88. package/js/vm/factory.ts +615 -0
  89. package/js/vm/headless-vm.test.ts +131 -0
  90. package/js/vm/headless-vm.ts +342 -0
  91. package/js/vm/index.test.ts +28 -0
  92. package/js/vm/index.ts +5 -0
  93. package/js/vm/internal-types.ts +32 -0
  94. package/js/vm/json.test.ts +40 -0
  95. package/js/vm/json.ts +273 -0
  96. package/js/vm/normalize.test.ts +48 -0
  97. package/js/vm/normalize.ts +65 -0
  98. package/js/vm/options.test.ts +30 -0
  99. package/js/vm/options.ts +55 -0
  100. package/js/vm/pen-transparency.test.ts +115 -0
  101. package/js/vm/program-wasm.ts +322 -0
  102. package/js/vm/scheduler-render.test.ts +401 -0
  103. package/js/vm/scratch-assets.test.ts +136 -0
  104. package/js/vm/scratch-assets.ts +202 -0
  105. package/js/vm/types.ts +358 -0
  106. package/js/vm/value-guards.test.ts +25 -0
  107. package/js/vm/value-guards.ts +18 -0
  108. package/moon.mod.json +10 -0
  109. package/package.json +33 -0
  110. package/scripts/preinstall.ts +4 -0
  111. package/src/AGENTS.md +6 -0
  112. package/src/api.mbt +161 -0
  113. package/src/api_aot_commands.mbt +184 -0
  114. package/src/api_effects_json.mbt +72 -0
  115. package/src/api_options.mbt +60 -0
  116. package/src/api_program_wasm.mbt +1647 -0
  117. package/src/api_program_wat.mbt +2206 -0
  118. package/src/api_snapshot_json.mbt +44 -0
  119. package/src/cmd/AGENTS.md +5 -0
  120. package/src/cmd/main/AGENTS.md +5 -0
  121. package/src/cmd/main/main.mbt +29 -0
  122. package/src/cmd/main/moon.pkg +7 -0
  123. package/src/cmd/main/pkg.generated.mbti +13 -0
  124. package/src/json_helpers.mbt +176 -0
  125. package/src/moon.pkg +65 -0
  126. package/src/moonscratch.mbt +3 -0
  127. package/src/moonscratch_wbtest.mbt +40 -0
  128. package/src/parser_sb3.mbt +890 -0
  129. package/src/pkg.generated.mbti +479 -0
  130. package/src/runtime_eval.mbt +2844 -0
  131. package/src/runtime_exec.mbt +3850 -0
  132. package/src/runtime_render.mbt +2550 -0
  133. package/src/runtime_state.mbt +870 -0
  134. package/src/test/AGENTS.md +3 -0
  135. package/src/test/projects/AGENTS.md +6 -0
  136. package/src/test/projects/moon.pkg +4 -0
  137. package/src/test/projects/moonscratch_compat_test.mbt +642 -0
  138. package/src/test/projects/moonscratch_core_test.mbt +1332 -0
  139. package/src/test/projects/moonscratch_runtime_test.mbt +1087 -0
  140. package/src/test/projects/pkg.generated.mbti +13 -0
  141. package/src/test/projects/test_support.mbt +35 -0
  142. package/src/types_effects.mbt +20 -0
  143. package/src/types_error.mbt +4 -0
  144. package/src/types_options.mbt +31 -0
  145. package/src/types_runtime_structs.mbt +254 -0
  146. package/src/types_vm.mbt +109 -0
  147. package/tsconfig.json +29 -0
  148. package/viewer/index.ts +399 -0
  149. package/viewer/vite.d.ts +1 -0
  150. package/viewer/worker.ts +161 -0
  151. package/vite.config.ts +11 -0
@@ -0,0 +1,1126 @@
1
+ ---
2
+ name: moonbit-agent-guide
3
+ description: Guide for writing, refactoring, and testing MoonBit projects. Use when working in MoonBit modules or packages, organizing MoonBit files, using moon tooling (build/check/run/test/doc/ide etc.), or following MoonBit-specific layout, documentation, and testing conventions.
4
+ ---
5
+
6
+ # Agent Workflow
7
+
8
+ For fast, reliable task execution, follow this order:
9
+
10
+ 1. **Clarify goal and constraints**
11
+ - Confirm expected behavior, non-goals, and compatibility constraints (target backend, public API stability, performance limits).
12
+
13
+ 2. **Locate module/package boundaries**
14
+ - Find `moon.mod.json` (module root) and relevant `moon.pkg`/`moon.pkg.json` files (package boundaries and imports).
15
+
16
+ 3. **Discover APIs before coding**
17
+ - Prefer `moon ide doc` queries to discover existing functions/types/methods before adding new code.
18
+ - Use `moon ide outline`, `moon ide peek-def`, and `moon ide find-references` for semantic navigation.
19
+
20
+ 4. **Reliable refactoring**
21
+ - Use `moon ide rename` for semantic refactoring. If multiple symbols share a name, add `--loc filename:line:col`.
22
+ - Use `#deprecated` when old APIs should warn and be removed after migration.
23
+ - Use `#alias(old_api, deprecated)` when temporary backward compatibility is required during migration.
24
+ - Remove `#deprecated` and `#alias` shims once callers are migrated and warnings are gone.
25
+ 5. **Edit minimally and package-locally**
26
+ - Keep changes inside the correct package, use `///|` top-level delimiters, and split code into cohesive files.
27
+
28
+ 6. **Validate in a tight loop**
29
+ - Run `moon check` after edits.
30
+ - Run targeted tests with `moon test [dirname|filename] --filter 'glob'` and use `moon test --update` for snapshot changes.
31
+
32
+ 7. **Finalize before handoff**
33
+ - Run `moon fmt`.
34
+ - Run `moon info` to verify whether public APIs changed (`pkg.generated.mbti` diff).
35
+ - Report changed files, validation commands, and any remaining risks.
36
+
37
+ ## Fast Task Playbooks
38
+
39
+ Use the smallest playbook that matches the request.
40
+
41
+ ### Bug Fix (No API Change Intended)
42
+
43
+ 1. Reproduce or identify the failing behavior.
44
+ 2. Locate symbols with `moon ide outline`, `moon ide peek-def`, `moon ide find-references`.
45
+ 3. Implement minimal fix in the current package.
46
+ 4. Validate with:
47
+ - `moon check`
48
+ - `moon test [dirname|filename] --filter 'glob'` (or closest targeted test scope)
49
+ - `moon fmt`
50
+ - `moon info` (confirm `pkg.generated.mbti` unchanged)
51
+
52
+ ### Refactor (Behavior Preserving)
53
+
54
+ 1. Confirm behavior/API invariants first.
55
+ 2. Prefer semantic rename/navigation tools:
56
+ - `moon ide rename`
57
+ - `moon ide find-references`
58
+ - `moon ide peek-def`
59
+ - If multiple symbols share a name, use `moon ide rename <symbol> <new_name> --loc filename:line:col`.
60
+ 3. Keep edits package-local and file-organization-focused.
61
+ 4. Validate with:
62
+ - `moon check`
63
+ - `moon test [dirname|filename]`
64
+ - `moon fmt`
65
+ - `moon info` (API should remain unchanged unless requested)
66
+
67
+ ### New Feature or Public API
68
+
69
+ 1. Discover existing idioms with `moon ide doc` before introducing new names.
70
+ 2. Add implementation in cohesive files with `///|` delimiters.
71
+ 3. Add/extend black-box tests and docstring examples for public APIs.
72
+ 4. Validate with:
73
+ - `moon check`
74
+ - `moon test [dirname|filename]` (use `--update` for snapshots when needed)
75
+ - `moon fmt`
76
+ - `moon info` (review and keep intended `pkg.generated.mbti` changes)
77
+
78
+ # MoonBit Project Layouts
79
+
80
+ MoonBit uses the `.mbt` extension for source code files and interface files with the `.mbti` extension. At
81
+ the top-level of a MoonBit project there is a `moon.mod.json` file specifying
82
+ the metadata of the project. The project may contain multiple packages, each
83
+ with its own `moon.pkg` or `moon.pkg.json` (legacy mode). Subdirectories may also contain `moon.mod.json`
84
+ files indicating that a different set of dependencies can be used for that subdir.
85
+
86
+ ## Example layout
87
+
88
+ ```
89
+ my_module
90
+ ├── moon.mod.json # Module metadata, source field (optional) specifies the source directory of the module
91
+ ├── moon.pkg # Package metadata (each directory is a package like Golang)
92
+ ├── README.mbt.md # Markdown with tested code blocks (`test "..." { ... }`)
93
+ ├── README.md -> README.mbt.md
94
+ ├── cmd # Command line directory
95
+ │ └── main
96
+ │ ├── main.mbt
97
+ │ └── moon.pkg # executable package with `options("is-main": true)`
98
+ ├── liba/ # Library packages
99
+ │ └── moon.pkg # Referenced by other packages as `@username/my_module/liba`
100
+ │ └── libb/ # Library packages
101
+ │ └── moon.pkg # Referenced by other packages as `@username/my_module/liba/libb`
102
+ ├── user_pkg.mbt # Root packages, referenced by other packages as `@username/my_module`
103
+ ├── user_pkg_wbtest.mbt # White-box tests (only needed for testing internal private members, similar to Golang's package mypackage)
104
+ └── user_pkg_test.mbt # Black-box tests
105
+ └── ... # More package files, symbols visible to current package (like Golang)
106
+ ```
107
+
108
+ - **Module**: characterized by a `moon.mod.json` file in the project root directory.
109
+ A MoonBit _module_ is like a Go module; it is a collection of packages in subdirectories, usually corresponding to a repository or project.
110
+ Module boundaries matter for dependency management and import paths.
111
+
112
+ - **Package**: characterized by a `moon.pkg` (or `moon.pkg.json`) file in each directory.
113
+ All subcommands of `moon` will
114
+ still be executed in the directory of the module (where `moon.mod.json` is
115
+ located), not the current package.
116
+ A MoonBit _package_ is the actual compilation unit (like a Go package).
117
+ All source files in the same package are concatenated into one unit and
118
+ thereby share all definitions throughout that package.
119
+ The `name` in the `moon.mod.json` file combined with the relative path to
120
+ the package source directory defines the package name, not the file name.
121
+ Imports refer to module + package paths, NEVER to file names.
122
+
123
+ - **Files**:
124
+ A `.mbt` file is just a chunk of source code inside a package.
125
+ File names do NOT create modules, packages, or namespaces.
126
+ You may freely split/merge/move declarations between files in the same package.
127
+ Any declaration in a package can reference any other declaration in that package, regardless of file.
128
+
129
+ ## Coding/layout rules you MUST follow:
130
+
131
+ 1. Prefer many small, cohesive files over one large file.
132
+ - Group related types and functions into focused files (e.g. http_client.mbt, router.mbt).
133
+ - If a file is getting large or unfocused, create a new file and move related declarations into it.
134
+
135
+ 2. You MAY freely move declarations between files inside the same package.
136
+ - Each block is separated by `///|`. Moving a function/struct/trait between files does not change semantics, as long as its name and pub-ness stay the same. The order of each block is irrelevant too.
137
+ - It is safe to refactor by splitting or merging files inside a package.
138
+
139
+ 3. File names are purely organizational.
140
+ - Do NOT assume file names define modules, and do NOT use file names in type paths.
141
+ - Choose file names to describe a feature or responsibility, not to mirror type names rigidly.
142
+
143
+ 4. When adding new code:
144
+ - Prefer adding it to an existing file that matches the feature.
145
+ - If no good file exists, create a new file under the same package with a descriptive name.
146
+ - Avoid creating giant "impl", “misc”, or “util” files.
147
+
148
+ 5. Tests:
149
+ - Place tests in dedicated test files (e.g. `*_test.mbt`) within the appropriate package.
150
+ For a package (besides `*_test.mbt`files), `*.mbt.md` files are also blackbox test files in addition to Markdown files.
151
+ The code blocks (separated by triple backticks) `mbt check` are treated as test cases and serve both purposes: documentation and tests.
152
+ You may have `README.mbt.md` files with `mbt check` code examples. You can also symlink `README.mbt.md` to `README.md`
153
+ to make it integrate better with GitHub.
154
+ - It is fine — and encouraged — to have multiple small test files.
155
+
156
+ 6. Interface files (`pkg.generated.mbti`)
157
+ `pkg.generated.mbti` files are compiler-generated summaries of each package's public API surface.
158
+ They provide a formal, concise overview of all exported types, functions, and traits without implementation details.
159
+ They are generated using `moon info` and useful for code review. When you have a commit that does not change public APIs, `pkg.generated.mbti` files will remain unchanged, so it is recommended to put `pkg.generated.mbti` in version control when you are done.
160
+
161
+ For IDE navigation and symbol lookup commands, see the dedicated `moon ide` section below.
162
+
163
+ # Common Pitfalls to Avoid
164
+
165
+ - **Don't use uppercase for variables/functions** - compilation error
166
+ - **Don't forget `mut` for mutable record fields** - immutable by default (note that Arrays typically do NOT need `mut` unless completely reassigning to the variable - simple push operations, for example, do not need `mut`)
167
+ - **Don't ignore error handling** - errors must be explicitly handled
168
+ - **Don't use `return` unnecessarily** - the last expression is the return value
169
+ - **Don't create methods without Type:: prefix** - methods need explicit type prefix
170
+ - **Don't forget to handle array bounds** - use `get()` for safe access
171
+ - **Don't forget @package prefix when calling functions from other packages**
172
+ - **Don't use ++ or -- (not supported)** - use `i = i + 1` or `i += 1`
173
+ - **Don't add explicit `try` for error-raising functions** - errors propagate automatically (unlike Swift)
174
+ - **Legacy syntax**: Older code may use `function_name!(...)` or `function_name(...)?` - these are deprecated; use normal calls and `try?` for Result conversion
175
+ - **Prefer range `for` loops over C-style** - `for i in 0..<(n-1) {...}` and `for j in 0..=6 {...}` are more idiomatic in MoonBit
176
+ - **Async** - MoonBit has no `await` keyword; do not add it. Async functions and tests are characterized by those which call other async functions.
177
+ To identify a function or test as async, simply add the `async` prefix (e.g. `[pub] async fn ...`, `async test ...`).
178
+
179
+ # `moon` Essentials
180
+
181
+ ## Essential Commands
182
+
183
+ - `moon new my_project` - Create new project
184
+ - `moon run cmd/main` - Run main package
185
+ - `moon build` - Build project
186
+ (`moon run` and `moon build` both support `--target`)
187
+ - `moon check` - Type check without building, use it REGULARLY, it is fast
188
+ (`moon check` also supports `--target`)
189
+ - `moon info` - Type check and generate `mbti` files.
190
+ Run it to see if any public interfaces changed.
191
+ (`moon info` also supports `--target`.)
192
+ - `moon check --target all` - Type check for all backends
193
+ - `moon add package` - Add dependency
194
+ - `moon remove package` - Remove dependency
195
+ - `moon fmt` - Format code - should be run periodically - note that the files may be rewritten
196
+ Note you can also use `moon -C dir check` to run commands in a specific directory.
197
+
198
+ ### Test Commands
199
+
200
+ - `moon test` - Run all tests
201
+ (`moon test` also supports `--target`)
202
+ - `moon test --update` - Update snapshots
203
+ - `moon test -v` - Verbose output with test names
204
+ - `moon test [dirname|filename]` - Test specific directory or file
205
+ - `moon coverage analyze` - Analyze coverage
206
+ - `moon test [dirname|filename] --filter 'glob'` - Run tests matching filter
207
+ ```
208
+ moon test float/float_test.mbt --filter "Float::*"
209
+ moon test float -F "Float::*" // shortcut syntax
210
+ ```
211
+
212
+ ## `README.mbt.md` Generation Guide
213
+
214
+ - Output `README.mbt.md` in the package directory.
215
+ `*.mbt.md` file and docstring contents treats `mbt check` specially.
216
+ `mbt check` block will be included directly as code and also run by `moon check` and `moon test`. If you don't want the code snippets to be checked, explicit `mbt nocheck` is preferred.
217
+ If you are only referencing types from the package, you should use `mbt nocheck` which will only be syntax highlighted.
218
+ Symlink `README.mbt.md` to `README.md` to adapt to systems that expect `README.md`.
219
+
220
+ ## Testing Guide
221
+
222
+ Use snapshot tests as it is easy to update when behavior changes.
223
+
224
+ - **Snapshot Tests**: `inspect(value, content="...")`. If unknown, write `inspect(value)` and run `moon test --update` (or `moon test -u`).
225
+ - Use regular `inspect()` for simple values (uses `Show` trait)
226
+ - Use `@json.inspect()` for complex nested structures (uses `ToJson` trait, produces more readable output)
227
+ - It is encouraged to `inspect` or `@json.inspect` the whole return value of a function if
228
+ the whole return value is not huge, this makes the test simple. You need `impl (Show|ToJson) for YourType` or `derive (Show, ToJson)`.
229
+ - **Update workflow**: After changing code that affects output, run `moon test --update` to regenerate snapshots, then review the diffs in your test files (the `content=` parameter will be updated automatically).
230
+ - **Validation order**: Follow the canonical sequence in `Agent Workflow` and `Fast Task Playbooks`.
231
+
232
+ - Black-box by default: Call only public APIs via `@package.fn`. Use white-box tests only when private members matter.
233
+ - Grouping: Combine related checks in one `test "..." { ... }` block for speed and clarity.
234
+ - Panics: Name tests with prefix `test "panic ..." {...}`; if the call returns a value, wrap it with `ignore(...)` to silence warnings.
235
+ - Errors: Use `try? f()` to get `Result[...]` and `inspect` it when a function may raise.
236
+
237
+ ### Docstring tests
238
+
239
+ Public APIs are encouraged to have docstring tests.
240
+
241
+ ````mbt check
242
+ ///|
243
+ /// Get the largest element of a non-empty `Array`.
244
+ ///
245
+ /// # Example
246
+ /// ```mbt check
247
+ /// test {
248
+ /// inspect(sum_array([1, 2, 3, 4, 5, 6]), content="21")
249
+ /// }
250
+ /// ```
251
+ ///
252
+ /// # Panics
253
+ /// Panics if the `xs` is empty.
254
+ pub fn sum_array(xs : Array[Int]) -> Int {
255
+ xs.fold(init=0, (a, b) => a + b)
256
+ }
257
+ ````
258
+
259
+ The MoonBit code in a docstring will be type checked and tested automatically
260
+ (using `moon test --update`). In docstrings, `mbt check` should only contain `test` or `async test`.
261
+
262
+ ## Spec-driven Development
263
+
264
+ - The spec can be written in a readonly `spec.mbt` file (name is conventional, not mandatory) with stub code marked as declarations:
265
+
266
+ ```mbt check
267
+ ///|
268
+ declare pub type Yaml
269
+
270
+ ///|
271
+ declare pub fn Yaml::to_string(y : Yaml) -> String raise
272
+
273
+ ///|
274
+ declare pub impl Eq for Yaml
275
+
276
+ ///|
277
+ declare pub fn parse_yaml(s : String) -> Yaml raise
278
+ ```
279
+
280
+ - Add `spec_easy_test.mbt`, `spec_difficult_test.mbt`, etc. to test the spec functions; everything will be type-checked(`moon check`).
281
+ - The AI or users can implement the `declare` functions in different files thanks to our package organization.
282
+ - Run `moon test` to check everything is correct.
283
+
284
+ - `declare` is supported for functions, methods, and types.
285
+ - The `pub type Yaml` line is an intentionally opaque placeholder; the implementer chooses its representation.
286
+ - Note the spec file can also contain normal code, not just declarations.
287
+
288
+ ## `moon ide [doc|peek-def|outline|find-references|hover|rename]` for code navigation and refactoring
289
+
290
+ For project-local symbols and navigation, use:
291
+
292
+ - `moon ide doc <query>` to discover available APIs, functions, types, and methods in MoonBit. Always prefer `moon ide doc` over other approaches when exploring what APIs are available, it is **more powerful and accurate** than `grep_search` or any regex-based searching tools.
293
+ - `moon ide outline .` to scan a package,
294
+ - `moon ide find-references <symbol>` to locate usages, and
295
+ - `moon ide peek-def` for inline definition context and to locate toplevel symbols.
296
+ - `moon ide hover sym --loc filename:line:col` to get type information at a specific location.
297
+ - `moon ide rename <symbol> <new_name> [--loc filename:line:col]` to rename a symbol project-wide. Prefer `--loc` when symbol names are ambiguous.
298
+ These tools save tokens and are more precise than grepping (`grep` displays results in both definitions and call sites including comments too).
299
+
300
+ ### `moon ide doc` for API Discovery
301
+
302
+ `moon ide doc` uses a specialized query syntax designed for symbol lookup:
303
+
304
+ - **Empty query**: `moon ide doc ''`
305
+ - In a module: shows all available packages in current module, including dependencies and moonbitlang/core
306
+ - In a package: shows all symbols in current package
307
+ - Outside package: shows all available packages
308
+
309
+ - **Function/value lookup**: `moon ide doc "[@pkg.]value_or_function_name"`
310
+
311
+ - **Type lookup**: `moon ide doc "[@pkg.]Type_name"` (builtin type does not need package prefix)
312
+
313
+ - **Method/field lookup**: `moon ide doc "[@pkg.]Type_name::method_or_field_name"`
314
+
315
+ - **Package exploration**: `moon ide doc "@pkg"`
316
+ - Show package `pkg` and list all its exported symbols
317
+ - Example: `moon ide doc "@json"` - explore entire `@json` package
318
+ - Example: `moon ide doc "@encoding/utf8"` - explore nested package
319
+
320
+ - **Globbing**: Use `*` wildcard for partial matches, e.g. `moon ide doc "String::*rev*"` to find all String methods with "rev" in their name
321
+
322
+ #### `moon ide doc` Examples
323
+
324
+ ```bash
325
+ # search for String methods in standard library:
326
+ $ moon ide doc "String"
327
+
328
+ type String
329
+
330
+ pub fn String::add(String, String) -> String
331
+ # ... more methods omitted ...
332
+
333
+ $ moon ide doc "@buffer" # list all symbols in package buffer:
334
+ moonbitlang/core/buffer
335
+
336
+ fn from_array(ArrayView[Byte]) -> Buffer
337
+ # ... omitted ...
338
+
339
+ $ moon ide doc "@buffer.new" # list the specific function in a package:
340
+ package "moonbitlang/core/buffer"
341
+
342
+ pub fn new(size_hint? : Int) -> Buffer
343
+ Creates ... omitted ...
344
+
345
+
346
+ $ moon ide doc "String::*rev*" # globbing
347
+ package "moonbitlang/core/string"
348
+
349
+ pub fn String::rev(String) -> String
350
+ Returns ... omitted ...
351
+ # ... more
352
+
353
+ pub fn String::rev_find(String, StringView) -> Int?
354
+ Returns ... omitted ...
355
+ ```
356
+
357
+ **Best practice**: Treat this section as command reference; execution order is defined in `Agent Workflow`.
358
+
359
+ ### `moon ide rename sym new_name [--loc filename:line:col]` example
360
+
361
+ When the user asks: "Can you rename the function `compute_sum` to `calculate_sum`?"
362
+
363
+ ```
364
+ $ moon ide rename compute_sum calculate_sum --loc math_utils.mbt:2
365
+
366
+ *** Begin Patch
367
+ *** Update File: cmd/main/main.mbt
368
+ @@
369
+ ///|
370
+ fn main {
371
+ - println(@math_utils.compute_sum(1, 2))
372
+ + println(@math_utils.calculate_sum(1, 2))
373
+ }
374
+ *** Update File: math_utils.mbt
375
+ @@
376
+ ///|
377
+ -pub fn compute_sum(a: Int, b: Int) -> Int {
378
+ +pub fn calculate_sum(a: Int, b: Int) -> Int {
379
+ a + b
380
+ }
381
+ *** Update File: math_utils_test.mbt
382
+ @@
383
+ ///|
384
+ test {
385
+ - inspect(@math_utils.compute_sum(1, 2))
386
+ + inspect(@math_utils.calculate_sum(1, 2))
387
+ }
388
+ *** End Patch
389
+ ```
390
+
391
+ ### `moon ide hover sym --loc filename:line:col` example
392
+
393
+ When the user asks: "What is the signature and docstring of `filter`? at line 14 of hover.mbt"
394
+
395
+ ````
396
+ $ moon ide hover filter --loc hover.mbt:14
397
+ test {
398
+ let a: Array[Int] = [1]
399
+ inspect(a.filter((x) => {x > 1}))
400
+ ^^^^^^
401
+ ```moonbit
402
+ fn[T] Array::filter(self : Array[T], f : (T) -> Bool raise?) -> Array[T] raise?
403
+ ```
404
+ ---
405
+
406
+ Creates a new array containing all elements from the input array that satisfy
407
+ ... omitted ...
408
+ }
409
+ ````
410
+
411
+ ### `moon ide peek-def sym [--loc filename:line:col]` example
412
+
413
+ When the user asks: "Can you check if `Parser::read_u32_leb128` is implemented correctly?"
414
+ you can run `moon ide peek-def Parser::read_u32_leb128` to get the definition context
415
+ (this is better than `grep` since it searches the whole project by semantics):
416
+
417
+ ```file src/parse.mbt
418
+ L45:|///|
419
+ L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
420
+ L47:| ...
421
+ ...:| }
422
+ ```
423
+
424
+ Now if you want to see the definition of the `Parser` struct, you can run:
425
+
426
+ ```bash
427
+ $ moon ide peek-def Parser --loc src/parse.mbt:46:4
428
+ Definition found at file src/parse.mbt
429
+ | ///|
430
+ 2 | priv struct Parser {
431
+ | ^^^^^^
432
+ | bytes : Bytes
433
+ | mut pos : Int
434
+ | }
435
+ |
436
+ ```
437
+
438
+ For the `--loc` argument, the line number must be precise; the column can be approximate since
439
+ the positional argument `Parser` helps locate the position.
440
+
441
+ If the "sym" is a toplevel symbol, the location can be omitted:
442
+
443
+ ```bash
444
+ $ moon ide peek-def String::rev
445
+ Found 1 symbols matching 'String::rev':
446
+
447
+ `pub fn String::rev` in package moonbitlang/core/builtin at /Users/usrname/.moon/lib/core/builtin/string_methods.mbt:1039-1044
448
+ 1039 | ///|
449
+ | /// Returns a new string with the characters in reverse order. It respects
450
+ | /// Unicode characters and surrogate pairs but not grapheme clusters.
451
+ | pub fn String::rev(self : String) -> String {
452
+ | self[:].rev()
453
+ | }
454
+ ```
455
+
456
+ ### `moon ide outline [dir|file]` and `moon ide find-references <sym>` for Package Symbols
457
+
458
+ Use `moon ide outline` to scan a package or file for top-level symbols and locate usages without grepping.
459
+
460
+ - `moon ide outline dir` outlines the current package directory (per-file headers)
461
+ - `moon ide outline parser.mbt` outlines a single file
462
+ This is useful when you need a quick inventory of a package, or to find the right file before `goto-definition`.
463
+ - `moon ide find-references TranslationUnit` finds all references to a symbol in the current module
464
+
465
+ ```bash
466
+ $ moon ide outline .
467
+ spec.mbt:
468
+ L003 | pub(all) enum CStandard {
469
+ ...
470
+ L013 | pub(all) struct Position {
471
+ ...
472
+ ```
473
+
474
+ ```bash
475
+ $ moon ide find-references TranslationUnit
476
+ ```
477
+
478
+ ## Package Management
479
+
480
+ ### Adding Dependencies
481
+
482
+ ```sh
483
+ moon add moonbitlang/x # Add latest version
484
+ moon add moonbitlang/x@0.4.6 # Add specific version
485
+ ```
486
+
487
+ ### Updating Dependencies
488
+
489
+ ```sh
490
+ moon update # Update package index
491
+ ```
492
+
493
+ ### Typical Module configurations (`moon.mod.json`)
494
+
495
+ ```json
496
+ {
497
+ "name": "username/hello", // Required format for published modules
498
+ "version": "0.1.0",
499
+ "source": ".", // Source directory(optional, default: ".")
500
+ "repository": "", // Git repository URL
501
+ "keywords": [], // Search keywords
502
+ "description": "...", // Module description
503
+ "deps": {
504
+ // Dependencies from mooncakes.io, using`moon add` to add dependencies
505
+ "moonbitlang/x": "0.4.6"
506
+ }
507
+ }
508
+ ```
509
+
510
+ ### Typical Package configuration (`moon.pkg`)
511
+
512
+ moon.pkg for simplicity
513
+
514
+ ```
515
+ import {
516
+ "username/hello/liba",
517
+ "moonbitlang/x/encoding" @libb
518
+ }
519
+ import {...} for "test"
520
+ import {...} for "wbtest"
521
+ options("is-main" : true) // other options
522
+ ```
523
+
524
+ or moon.pkg.json (legacy mode)
525
+
526
+ ```json
527
+ {
528
+ "is_main": true, // Creates executable when true
529
+ "import": [ // Package dependencies
530
+ "username/hello/liba", // Simple import, use @liba.foo() to call functions
531
+ {
532
+ "path": "moonbitlang/x/encoding",
533
+ "alias": "libb" // Custom alias, use @libb.encode() to call functions
534
+ }
535
+ ],
536
+ "test-import": [...], // Imports for black-box tests, similar to import
537
+ "wbtest-import": [...] // Imports for white-box tests, similar to import (rarely used)
538
+ }
539
+ ```
540
+
541
+ Packages are per directory and packages without a `moon.pkg` or `moon.pkg.json` file are not recognized.
542
+
543
+ ### Package Importing (used in moon.pkg)
544
+
545
+ - **Import format**: `"module_name/package_path"`
546
+ - **Usage**: `@alias.function()` to call imported functions
547
+ - **Default alias**: Last part of path (e.g., `liba` for `username/hello/liba`)
548
+ - **Package reference**: Use `@packagename` in test files to reference the
549
+ tested package
550
+
551
+ **Package Alias Rules**:
552
+
553
+ - Import `"username/hello/liba"` → use `@liba.function()` (default alias is the last path segment)
554
+ - Import with custom alias `import { "moonbitlang/x/encoding" @enc}` → use `@enc.function()`
555
+ (Note that this is unnecessary when the last path segment is identical to the alias name.)
556
+ - In `_test.mbt` or `_wbtest.mbt` files, the package being tested is auto-imported
557
+
558
+ Example:
559
+
560
+ ```mbt
561
+ ///|
562
+ /// In main.mbt after importing "username/hello/liba" in `moon.pkg`
563
+ fn main {
564
+ println(@liba.hello()) // Calls hello() from liba package
565
+ }
566
+ ```
567
+
568
+ ### Using the Standard Library (moonbitlang/core)
569
+
570
+ **MoonBit standard library (moonbitlang/core) packages were automatically imported**. MoonBit is transitioning to explicit imports—you will see a warning to add imports like `moonbitlang/core/strconv` to `moon.pkg` if you use them.
571
+ The module is always available without adding to dependencies.
572
+
573
+ ### Creating Packages
574
+
575
+ To add a new package `fib` under `.`:
576
+
577
+ 1. Create directory: `./fib/`
578
+ 2. Add `./fib/moon.pkg`
579
+ 3. Add `.mbt` files with your code
580
+ 4. Import in dependent packages:
581
+
582
+ ```
583
+ import {
584
+ "username/hello/fib",
585
+ }
586
+ ```
587
+
588
+ For more advanced topics like `conditional compilation`, `link configuration`, `warning control`, and `pre-build commands`, see `references/advanced-moonbit-build.md`.
589
+
590
+ # MoonBit Language Tour
591
+
592
+ ## Core facts
593
+
594
+ - **Expression‑oriented**: `if`, `match`, loops return values; the last expression is the return value.
595
+ - **References by default**: Arrays/Maps/structs mutate via reference; use `Ref[T]` for primitive mutability.
596
+ - **Blocks**: Separate top‑level items with `///|`. Generate code block‑by‑block.
597
+ If a blank line is desired within a block (enclosed by curly braces), add a comment line after the blank line (with or without comment text).
598
+ - **Visibility**: `fn` is private by default; `pub` exposes read/construct as allowed; `pub(all)` allows external construction.
599
+ - **Naming convention**: lower_snake for values/functions; UpperCamel for types/enums; enum variants start UpperCamel.
600
+ - **Packages**: No `import` in code files; call via `@alias.fn`. Configure imports in `moon.pkg`.
601
+ - **Placeholders**: `...` is a valid placeholder in MoonBit code for incomplete implementations.
602
+ - **Global values**: immutable by default and generally require type annotations.
603
+ - **Garbage collection**: MoonBit has a GC, there is no lifetime annotation, there's no ownership system.
604
+ Unlike Rust, like F#, `let mut` is only needed when you want to reassign a variable, not for mutating fields of a struct or elements of an array/map.
605
+
606
+ ## MoonBit Error Handling (Checked Errors)
607
+
608
+ MoonBit uses checked error-throwing functions, not unchecked exceptions. All errors are a subtype of `Error` and you can declare your own error types using `suberror`.
609
+ Use `raise` in signatures to declare error types and let errors propagate by
610
+ default. Use `try?` to convert to `Result[...]` in tests, or `try { } catch { }`
611
+ to handle errors explicitly. Use `try!` to abort if it does raise.
612
+
613
+ ```mbt check
614
+ ///|
615
+ /// Declare error types with 'suberror'
616
+ suberror ValueError {
617
+ ValueError(String)
618
+ }
619
+
620
+ ///|
621
+ /// Tuple struct to hold position info
622
+ struct Position(Int, Int) derive(ToJson, Show, Eq)
623
+
624
+ ///|
625
+ /// ParseError is subtype of Error
626
+ pub(all) suberror ParseError {
627
+ InvalidChar(pos~ : Position, Char) // pos is labeled
628
+ InvalidEof(pos~ : Position)
629
+ InvalidNumber(pos~ : Position, String)
630
+ InvalidIdentEscape(pos~ : Position)
631
+ } derive(Eq, ToJson, Show)
632
+
633
+ ///|
634
+ /// Functions declare what they can throw
635
+ fn parse_int(s : String, position~ : Position) -> Int raise ParseError {
636
+ // 'raise' throws an error
637
+ if s is "" {
638
+ raise ParseError::InvalidEof(pos=position)
639
+ }
640
+ ... // parsing logic
641
+ }
642
+
643
+ ///|
644
+ /// Just declare `raise` to not track specific error types
645
+ fn div(x : Int, y : Int) -> Int raise {
646
+ if y is 0 {
647
+ fail("Division by zero")
648
+ }
649
+ x / y
650
+ }
651
+
652
+ ///|
653
+ test "inspect raise function" {
654
+ let result : Result[Int, Error] = try? div(1, 0)
655
+ guard result is Err(Failure(msg)) && msg.contains("Division by zero") else {
656
+ fail("Expected error")
657
+ }
658
+ }
659
+
660
+ // Three ways to handle errors:
661
+
662
+ ///|
663
+ /// Propagate automatically
664
+ fn use_parse(position~ : Position) -> Int raise ParseError {
665
+ let x = parse_int("123", position~) // label punning, equivalent to position=position
666
+ // Error auto-propagates by default.
667
+ // Unlike Swift, you do not need to mark `try` for functions that can raise
668
+ // errors; the compiler infers it automatically. This keeps error handling
669
+ // explicit but concise.
670
+ x * 2
671
+ }
672
+
673
+ ///|
674
+ /// Mark `raise` for all possible errors, do not care which error it is.
675
+ /// For quick prototypes, `raise` is acceptable.
676
+ fn use_parse2(position~ : Position) -> Int raise {
677
+ let x = parse_int("123", position~) // label punning
678
+ x * 2
679
+ }
680
+
681
+ ///|
682
+ /// Convert to Result with try?
683
+ fn safe_parse(s : String, position~ : Position) -> Result[Int, ParseError] {
684
+ let val1 : Result[_] = try? parse_int(s, position~) // Returns Result[Int, ParseError]
685
+ // try! is rarely used - it panics on error, similar to unwrap() in Rust
686
+ // let val2 : Int = try! parse_int(s) // Returns Int otherwise crash
687
+
688
+ // Alternative explicit handling:
689
+ let val3 = try parse_int(s, position~) catch {
690
+ err => Err(err)
691
+ } noraise { // noraise block is optional - handles the success case
692
+ v => Ok(v)
693
+ }
694
+ ...
695
+ }
696
+
697
+ ///|
698
+ /// Handle with try-catch
699
+ fn handle_parse(s : String, position~ : Position) -> Int {
700
+ try parse_int(s, position~) catch {
701
+ ParseError::InvalidEof => {
702
+ println("Parse failed: InvalidEof")
703
+ -1 // Default value
704
+ }
705
+ _ => 2
706
+ }
707
+ }
708
+ ```
709
+
710
+ Important: When calling a function that can raise errors, if you only want to
711
+ propagate the error, you do not need any marker; the compiler infers it.
712
+ Note that all `async` functions automatically can raise errors without explicitly stating this.
713
+
714
+ ## Integers, Char
715
+
716
+ MoonBit supports `Byte`, `Int16`, `Int`, `UInt16`, `UInt`, `Int64`, `UInt64`, etc.
717
+ When the type is known, the literal can be overloaded:
718
+
719
+ ```mbt check
720
+ ///|
721
+ test "integer and char literal overloading disambiguation via type in the current context" {
722
+ let a0 = 1 // a is Int by default
723
+ let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
724
+ 1, 1, 1, 1, 1,
725
+ )
726
+ assert_eq(int, uint16.to_int())
727
+ let a1 : Int = 'b' // this also works, a5 will be the unicode value
728
+ let a2 : Char = 'b'
729
+
730
+ }
731
+ ```
732
+
733
+ ## Bytes (Immutable)
734
+
735
+ ```mbt check
736
+ ///|
737
+ test "bytes literals overloading and indexing" {
738
+ let b0 : Bytes = b"abcd"
739
+ let b1 : Bytes = "abcd" // b" prefix is optional, when we know the type
740
+ let b2 : Bytes = [0xff, 0x00, 0x01] // Array literal overloading
741
+ guard b0 is [b'a', ..] && b0[1] is b'b' else {
742
+ // Bytes can be pattern matched as BytesView and indexed
743
+ fail("unexpected bytes content")
744
+ }
745
+ }
746
+ ```
747
+
748
+ ## Array (Resizable)
749
+
750
+ ```mbt check
751
+ ///|
752
+ test "array literals overloading: disambiguation via type in the current context" {
753
+ let a0 : Array[Int] = [1, 2, 3] // resizable
754
+ let a1 : FixedArray[Int] = [1, 2, 3] // Fixed size
755
+ let a2 : ReadOnlyArray[Int] = [1, 2, 3]
756
+ let a3 : ArrayView[Int] = [1, 2, 3]
757
+
758
+ }
759
+ ```
760
+
761
+ ## String (Immutable UTF-16)
762
+
763
+ `s[i]` returns a code unit (UInt16), `s.get_char(i)` returns `Char?`.
764
+ Since MoonBit supports char literal overloading, you can write code snippets like this:
765
+
766
+ ```mbt check
767
+ ///|
768
+ test "string indexing and utf8 encode/decode" {
769
+ let s = "hello world"
770
+ let b0 : UInt16 = s[0]
771
+ guard b0 is ('\n' | 'h' | 'b' | 'a'..='z') && s is [.. "hello", .. rest] else {
772
+ fail("unexpected string content")
773
+ }
774
+ guard rest is " world" // otherwise will crash (guard without else)
775
+
776
+ // In check mode (expression with explicit type), ('\n' : UInt16) is valid.
777
+
778
+ // Using get_char for Option handling
779
+ let b1 : Char? = s.get_char(0)
780
+ assert_true(b1 is Some('a'..='z'))
781
+
782
+ // ⚠️ Important: Variables won't work with direct indexing
783
+ let eq_char : Char = '='
784
+ // s[0] == eq_char // ❌ Won't compile - eq_char is not a literal, lhs is UInt while rhs is Char
785
+ // Use: s[0] == '=' or s.get_char(0) == Some(eq_char)
786
+ let bytes = @utf8.encode("中文") // utf8 encode package is in stdlib
787
+ assert_true(bytes is [0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87])
788
+ let s2 : String = @utf8.decode(bytes) // decode utf8 bytes back to String
789
+ assert_true(s2 is "中文")
790
+ for c in "中文" {
791
+ let _ : Char = c // unicode safe iteration
792
+ println("char: \{c}") // iterate over chars
793
+ }
794
+ }
795
+ ```
796
+
797
+ ### String Interpolation && StringBuilder
798
+
799
+ MoonBit uses `\{}` for string interpolation, for custom types, they need to implement trait `Show`.
800
+
801
+ ```mbt check
802
+ ///|
803
+ test "string interpolation basics" {
804
+ let name : String = "Moon"
805
+ let config = { "cache": 123 }
806
+ let version = 1.0
807
+ println("Hello \{name} v\{version}") // "Hello Moon v1.0"
808
+ // ❌ Wrong - quotes inside interpolation not allowed:
809
+ // println(" - Checking if 'cache' section exists: \{config["cache"]}")
810
+
811
+ // ✅ Correct - extract to variable first:
812
+ let has_key = config["cache"] // `"` not allowed in interpolation
813
+ println(" - Checking if 'cache' section exists: \{has_key}")
814
+ let sb = StringBuilder::new()
815
+ sb
816
+ ..write_char('[') // dotdot for imperative method chaining
817
+ ..write_view([1, 2, 3].map(x => "\{x}").join(","))
818
+ ..write_char(']')
819
+ inspect(sb.to_string(), content="[1,2,3]")
820
+ }
821
+ ```
822
+
823
+ Expressions inside `\{}` can only be _basic expressions_ (no quotes, newlines, or nested interpolations). String literals are not allowed as they make lexing too difficult.
824
+
825
+ ### Multiple line strings
826
+
827
+ ```mbt check
828
+ ///|
829
+ test "multi-line string literals" {
830
+ let multi_line_string : String =
831
+ #|Hello "world"
832
+ #|World
833
+ #|
834
+ let multi_line_string_with_interp : String =
835
+ $|Line 1 ""
836
+ $|Line 2 \{1+2}
837
+ $|
838
+ // no escape in `#|`,
839
+ // only escape '\{..}` in `$|`
840
+ assert_eq(multi_line_string, "Hello \"world\"\nWorld\n")
841
+ assert_eq(multi_line_string_with_interp, "Line 1 \"\"\nLine 2 3\n")
842
+ }
843
+ ```
844
+
845
+ ## Map (Mutable, Insertion-Order Preserving)
846
+
847
+ ```mbt check
848
+ ///|
849
+ test "map literals and common operations" {
850
+ // Map literal syntax
851
+ let map : Map[String, Int] = { "a": 1, "b": 2, "c": 3 }
852
+ let empty : Map[String, Int] = {} // Empty map, preferred
853
+ let also_empty : Map[String, Int] = Map::new()
854
+ // From array of pairs
855
+ let from_pairs : Map[String, Int] = Map::from_array([("x", 1), ("y", 2)])
856
+
857
+ // Set/update value
858
+ map["new-key"] = 3
859
+ map["a"] = 10 // Updates existing key
860
+
861
+ // Get value - returns Option[T]
862
+ guard map is { "new-key": 3, "missing"? : None, .. } else {
863
+ fail("unexpected map contents")
864
+ }
865
+
866
+ // Direct access (panics if key missing)
867
+ let value : Int = map["a"] // value = 10
868
+
869
+ // Iteration preserves insertion order
870
+ for k, v in map {
871
+ println("\{k}: \{v}") // Prints: a: 10, b: 2, c: 3, new-key: 3
872
+ }
873
+
874
+ // Other common operations
875
+ map.remove("b")
876
+ guard map is { "a": 10, "c": 3, "new-key": 3, .. } && map.length() == 3 else {
877
+ // "b" is gone, only 3 elements left
878
+ fail("unexpected map contents after removal")
879
+ }
880
+ }
881
+ ```
882
+
883
+ ## View Types
884
+
885
+ **Key Concept**: View types (`StringView`, `BytesView`, `ArrayView[T]`) are zero-copy, non-owning read-only slices created with the `[:]` syntax. They don't allocate memory and are ideal for passing sub-sequences without copying data, for functions which take `String`, `Bytes`, `Array`, they also take `*View` (implicit conversion).
886
+
887
+ - `String` → `StringView` via `s[:]` or `s[start:end]` or `s[start:]` or `s[:end]`
888
+ - `Bytes` → `BytesView` via `b[:]` or `b[start:end]`, etc.
889
+ - `Array[T]`, `FixedArray[T]`, `ReadOnlyArray[T] → `ArrayView[T]`via`a[:]`or`a[start:end]`, etc.
890
+
891
+ **Important**: StringView slice is slightly different due to unicode safety:
892
+ `s[a:b]` may raise an error at surrogate boundaries (UTF-16 encoding edge case). You have two options:
893
+
894
+ - Use `try! s[a:b]` if you're certain the boundaries are valid (crashes on invalid boundaries)
895
+ - Let the error propagate to the caller for proper handling
896
+
897
+ **When to use views**:
898
+
899
+ - Pattern matching with rest patterns (`[first, .. rest]`)
900
+ - Passing slices to functions without allocation overhead
901
+ - Avoiding unnecessary copies of large sequences
902
+
903
+ Convert back with `.to_string()`, `.to_bytes()`, or `.to_array()` when you need ownership. (`moon ide doc StringView`)
904
+
905
+ ## User defined types(`enum`, `struct`)
906
+
907
+ ```mbt check
908
+ ///|
909
+ enum Tree[T] {
910
+ Leaf(T) // Unlike Rust, no comma here
911
+ Node(left~ : Tree[T], T, right~ : Tree[T]) // enum can use labels
912
+ } derive(Show, ToJson) // derive traits for Tree
913
+
914
+ ///|
915
+ pub fn Tree::sum(tree : Tree[Int]) -> Int {
916
+ match tree {
917
+ Leaf(x) => x
918
+ // we don't need to write Tree::Leaf, when `tree` has a known type
919
+ Node(left~, x, right~) => left.sum() + x + right.sum() // method invoked in dot notation
920
+ }
921
+ }
922
+
923
+ ///|
924
+ struct Point {
925
+ x : Int
926
+ y : Int
927
+ } derive(Show, ToJson) // derive traits for Point
928
+
929
+ ///|
930
+ test "user defined types: enum and struct" {
931
+ @json.inspect(Point::{ x: 10, y: 20 }, content={ "x": 10, "y": 20 })
932
+ }
933
+ ```
934
+
935
+ ## Functional `for` loop
936
+
937
+ ```mbt check
938
+ ///|
939
+ pub fn binary_search(arr : ArrayView[Int], value : Int) -> Result[Int, Int] {
940
+ let len = arr.length()
941
+ // functional for loop:
942
+ // initial state ; [predicate] ; [post-update] {
943
+ // loop body with `continue` to update state
944
+ //} else { // exit block
945
+ // }
946
+ // predicate and post-update are optional
947
+ for i = 0, j = len; i < j; {
948
+ // post-update is omitted, we use `continue` to update state
949
+ let h = i + (j - i) / 2
950
+ if arr[h] < value {
951
+ continue h + 1, j // functional update of loop state
952
+ } else {
953
+ continue i, h // functional update of loop state
954
+ }
955
+ } else { // exit of for loop
956
+ if i < len && arr[i] == value {
957
+ Ok(i)
958
+ } else {
959
+ Err(i)
960
+ }
961
+ } where {
962
+ invariant: 0 <= i && i <= j && j <= len,
963
+ invariant: i == 0 || arr[i - 1] < value,
964
+ invariant: j == len || arr[j] >= value,
965
+ reasoning: (
966
+ #|For a sorted array, the boundary invariants are witnesses:
967
+ #| - `arr[i-1] < value` implies all arr[0..i) < value (by sortedness)
968
+ #| - `arr[j] >= value` implies all arr[j..len) >= value (by sortedness)
969
+ #|
970
+ #|Preservation proof:
971
+ #| - When arr[h] < value: new_i = h+1, and arr[new_i - 1] = arr[h] < value ✓
972
+ #| - When arr[h] >= value: new_j = h, and arr[new_j] = arr[h] >= value ✓
973
+ #|
974
+ #|Termination: j - i decreases each iteration (h is strictly between i and j)
975
+ #|
976
+ #|Correctness at exit (i == j):
977
+ #| - By invariants: arr[0..i) < value and arr[i..len) >= value
978
+ #| - So if value exists, it can only be at index i
979
+ #| - If arr[i] != value, then value is absent and i is the insertion point
980
+ #|
981
+ ),
982
+ }
983
+ }
984
+
985
+ ///|
986
+ test "functional for loop control flow" {
987
+ let arr : Array[Int] = [1, 3, 5, 7, 9]
988
+ inspect(binary_search(arr, 5), content="Ok(2)") // Array to ArrayView implicit conversion when passing as arguments
989
+ inspect(binary_search(arr, 6), content="Err(3)")
990
+ // for iteration is supported too
991
+ for i, v in arr {
992
+ println("\{i}: \{v}") // `i` is index, `v` is value
993
+ }
994
+ }
995
+ ```
996
+
997
+ You are _STRONGLY ENCOURAGED_ to use functional `for` loops instead of imperative loops
998
+ _WHENEVER POSSIBLE_, as they are easier to read and reason about.
999
+
1000
+ ### Loop Invariants with `where` Clause
1001
+
1002
+ The `where` clause attaches **machine-checkable invariants** and **human-readable reasoning** to functional `for` loops. This enables formal verification thinking while keeping the code executable. Note for trivial loops, you are encouraged to convert it into `for .. in` so no reasoning is needed.
1003
+
1004
+ **Syntax:**
1005
+
1006
+ ```mbt nocheck
1007
+ for ... {
1008
+ ...
1009
+ } where {
1010
+ invariant : <boolean_expr>, // checked at runtime in debug builds
1011
+ invariant : <boolean_expr>, // multiple invariants allowed
1012
+ reasoning : <string> // documentation for proof sketch
1013
+ }
1014
+ ```
1015
+
1016
+ **Writing Good Invariants:**
1017
+
1018
+ 1. **Make invariants checkable**: Invariants must be valid MoonBit boolean expressions using loop variables and captured values.
1019
+
1020
+ 2. **Use boundary witnesses**: For properties over ranges (e.g., "all elements in arr[0..i) satisfy P"), check only boundary elements. For sorted arrays, `arr[i-1] < value` implies all `arr[0..i) < value`.
1021
+
1022
+ 3. **Handle edge cases with `||`**: Use patterns like `i == 0 || arr[i-1] < value` to handle boundary conditions where the check would be out of bounds.
1023
+
1024
+ 4. **Cover three aspects in reasoning**:
1025
+ - **Preservation**: Why each `continue` maintains the invariants
1026
+ - **Termination**: Why the loop eventually exits (e.g., a decreasing measure)
1027
+ - **Correctness**: Why the invariants at exit imply the desired postcondition
1028
+
1029
+ ## Label and Optional Parameters
1030
+
1031
+ Good example: use labeled and optional parameters
1032
+
1033
+ ```mbt check
1034
+ ///|
1035
+ fn g(
1036
+ positional : Int,
1037
+ required~ : Int,
1038
+ optional? : Int, // no default => Option
1039
+ optional_with_default? : Int = 42, // default => plain Int
1040
+ ) -> String {
1041
+ // These are the inferred types inside the function body.
1042
+ let _ : Int = positional
1043
+ let _ : Int = required
1044
+ let _ : Int? = optional
1045
+ let _ : Int = optional_with_default
1046
+ "\{positional},\{required},\{optional},\{optional_with_default}"
1047
+ }
1048
+
1049
+ ///|
1050
+ test {
1051
+ inspect(g(1, required=2), content="1,2,None,42")
1052
+ inspect(g(1, required=2, optional=3), content="1,2,Some(3),42")
1053
+ inspect(g(1, required=4, optional_with_default=100), content="1,4,None,100")
1054
+ }
1055
+ ```
1056
+
1057
+ Misuse: `arg : Type?` is not an optional parameter.
1058
+ Callers still must pass it (as `None`/`Some(...)`).
1059
+
1060
+ ```mbt check
1061
+ ///|
1062
+ fn with_config(a : Int?, b : Int?, c : Int) -> String {
1063
+ "\{a},\{b},\{c}"
1064
+ }
1065
+
1066
+ ///|
1067
+ test {
1068
+ inspect(with_config(None, None, 1), content="None,None,1")
1069
+ inspect(with_config(Some(5), Some(5), 1), content="Some(5),Some(5),1")
1070
+ }
1071
+ ```
1072
+
1073
+ Anti-pattern: `arg? : Type?` (no default => double Option).
1074
+ If you want a defaulted optional parameter, write `b? : Int = 1`, not `b? : Int? = Some(1)`.
1075
+
1076
+ ```mbt check
1077
+ ///|
1078
+ fn f_misuse(a? : Int?, b? : Int = 1) -> Unit {
1079
+ let _ : Int?? = a // rarely intended
1080
+ let _ : Int = b
1081
+
1082
+ }
1083
+ // How to fix: declare `(a? : Int, b? : Int = 1)` directly.
1084
+
1085
+ ///|
1086
+ fn f_correct(a? : Int, b? : Int = 1) -> Unit {
1087
+ let _ : Int? = a
1088
+ let _ : Int = b
1089
+
1090
+ }
1091
+
1092
+ ///|
1093
+ test {
1094
+ f_misuse(b=3)
1095
+ f_misuse(a=Some(5), b=2) // works but confusing
1096
+ f_correct(b=2)
1097
+ f_correct(a=5)
1098
+ }
1099
+ ```
1100
+
1101
+ Bad example: `arg : APIOptions` (use labeled optional parameters instead)
1102
+
1103
+ ```mbt check
1104
+ ///|
1105
+ /// Do not use struct to group options.
1106
+ struct APIOptions {
1107
+ width : Int?
1108
+ height : Int?
1109
+ }
1110
+
1111
+ ///|
1112
+ fn not_idiomatic(opts : APIOptions, arg : Int) -> Unit {
1113
+
1114
+ }
1115
+
1116
+ ///|
1117
+ test {
1118
+ // Hard to use in call site
1119
+ not_idiomatic({ width: Some(5), height: None }, 10)
1120
+ not_idiomatic({ width: None, height: None }, 10)
1121
+ }
1122
+ ```
1123
+
1124
+ ## More details
1125
+
1126
+ For deeper syntax, types, and examples, read `references/moonbit-language-fundamentals.mbt.md`.