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,1469 @@
1
+ ----
2
+ title: MoonBit Project Layouts
3
+ ---
4
+
5
+ -
6
+
7
+ # MoonBit Project Layouts
8
+
9
+ You have the ability to detect specific types of MoonBit projects and work with
10
+ them adaptively.
11
+
12
+ MoonBit source files use the `.mbt` extension and interface files `.mbti`. At
13
+ the top-level of a MoonBit project there is a `moon.mod.json` file specifying
14
+ the metadata of the project. The project may contain multiple packages, each
15
+ with its own `moon.pkg.json` file.
16
+
17
+ Here are some typical project layouts you may encounter:
18
+
19
+ - **Module**: When you see a `moon.mod.json` file in the project directory, you
20
+ are already in a MoonBit project.
21
+ A MoonBit _module_ is like a Go module.
22
+ It is a collection of packages, usually corresponding to a repository or project.
23
+ Module boundaries matter for dependency management and import paths.
24
+ A module contains many packages in subdirectories.
25
+
26
+ - **Package**: When you see a `moon.pkg.json` file, but not a `moon.mod.json`
27
+ file, it means you are in a MoonBit package. All subcommands of `moon` will
28
+ still be executed in the directory of the module (where `moon.mod.json` is
29
+ located), not the current package.
30
+ A MoonBit _package_ is the actual compilation unit (like a Go package).
31
+ All source files in the same package are concatenated into one unit.
32
+ The `package` name in the source defines the package, not the file name.
33
+ Imports refer to module + package paths, NEVER to file names.
34
+
35
+ - **Files**:
36
+ A `.mbt` file is just a chunk of source inside a package.
37
+ File names do NOT create modules or namespaces.
38
+ You may freely split/merge/move declarations between files in the same package.
39
+ Any declaration in a package can reference any other declaration in that package, regardless of file.
40
+
41
+ ## Coding/layout rules you MUST follow:
42
+
43
+ 1. Prefer many small, cohesive files over one large file.
44
+ - Group related types and functions into focused files (e.g. http_client.mbt, router.mbt).
45
+ - If a file is getting large or unfocused, create a new file and move related declarations into it.
46
+
47
+ 2. You MAY freely move declarations between files inside the same package.
48
+ - Moving a function/struct/trait between files does not change semantics, as long as its name and pub-ness stay the same.
49
+ - It is safe to refactor by splitting or merging files inside a package.
50
+
51
+ 3. File names are purely organizational.
52
+ - Do NOT assume file names define modules, and do NOT use file names in type paths.
53
+ - Choose file names to describe a feature or responsibility, not to mirror type names rigidly.
54
+
55
+ 4. When adding new code:
56
+ - Prefer adding it to an existing file that matches the feature.
57
+ - If no good file exists, create a new file under the same package with a descriptive name.
58
+ - Avoid creating giant “misc” or “util” files.
59
+
60
+ 5. Tests:
61
+ - Place tests in dedicated test files (e.g. \*\_test.mbt) within the appropriate package.
62
+ - It is fine—and encouraged—to have multiple small test files.
63
+
64
+ ## `.mbti` Files - Package Interface Documentation
65
+
66
+ MoonBit interface files (`pkg.generated.mbti`) are compiler-generated summaries of each package's public API surface. They provide a formal, concise overview of all exported types, functions, and traits without implementation details.
67
+
68
+ **Standard library interfaces** are available in `~/.moon/lib/core`:
69
+
70
+ ```
71
+ $ tree -P '*.mbti' -I 'internal' --prune ~/.moon/lib/core # ignore internal packages
72
+ /Users/username/.moon/lib/core
73
+ ├── builtin
74
+ │ └── pkg.generated.mbti
75
+ ├── array
76
+ │ └── pkg.generated.mbti
77
+ ├── bench
78
+ │ └── pkg.generated.mbti
79
+ ├── bigint
80
+ │ └── pkg.generated.mbti
81
+ .....
82
+ ```
83
+
84
+ **When to use each approach**:
85
+
86
+ - Use `moon doc` for interactive API discovery (preferred, see "API Discovery with `moon doc`" section below)
87
+ - Read `.mbti` files directly when you need the complete API surface at once or when working offline
88
+
89
+ **Reading `.mbti` files for API discovery**:
90
+
91
+ - **Start with `builtin/pkg.generated.mbti`** - contains core types (String, Int, Array, etc.) and their fundamental APIs
92
+ - **Note**: Some builtin types like `String` expose APIs in both `builtin` and their dedicated packages (e.g., `String`)
93
+ - **Local dependencies**: Find `.mbti` files in the `.mooncakes` directory by searching for `pkg.generated.mbti`
94
+ - **Your own packages**: After running `moon info`, check the generated `.mbti` in each package directory to verify public API changes
95
+
96
+ # MoonBit Language Fundamentals
97
+
98
+ ## Core Facts
99
+
100
+ Core facts that impact how you write and refactor code.
101
+
102
+ - **Expression‑oriented**: `if`, `match`, loops return values; last expression is the return.
103
+ - **References by default**: Arrays/Maps/structs mutate via reference; use `Ref[T]` for primitive mutability.
104
+ - **Errors**: Functions declare `raise ...`; use `try?` for `Result` or `try { } catch { }` to handle.
105
+ - **Blocks**: Separate top‑level items with `///|`. Generate code block‑by‑block.
106
+ - **Visibility**: `fn` private by default; `pub` exposes read/construct as allowed; `pub(all)` allows external construction.
107
+ - **Naming convention**: lower_snake for values/functions; UpperCamel for types/enums; enum variants start UpperCamel.
108
+ - **Packages**: No `import` in code files; call via `@alias.fn`. Configure imports in `moon.pkg.json`.
109
+ - **Placeholders**: `...` is a valid placeholder in MoonBit code for incomplete implementations.
110
+ - **Global values**: immutable by default and generally require type annotations.
111
+ - **Garbage collection**: MoonBit has a GC, there is no lifetime annotation, there's no ownership system.
112
+ <Important> Delimit top-level items with `///|` comments so tools can split the file reliably.
113
+ </Important>
114
+
115
+ Quick reference:
116
+
117
+ ```mbt check
118
+ ///|
119
+ /// comments doc string
120
+ pub fn sum(x : Int, y : Int) -> Int {
121
+ x + y
122
+ }
123
+
124
+ ///|
125
+ /// error declaration and usage
126
+ suberror MySubError
127
+
128
+ ///|
129
+ fn risky() -> Int raise MySubError {
130
+ raise MySubError::MySubError
131
+ }
132
+
133
+ ///|
134
+ struct Rect {
135
+ width : Int
136
+ height : Int
137
+ }
138
+
139
+ ///|
140
+ fn Rect::area(self : Rect) -> Int {
141
+ self.width * self.height
142
+ }
143
+
144
+ ///|
145
+ pub impl Show for Rect with output(_self, logger) {
146
+ logger.write_string("Rect")
147
+ }
148
+
149
+ ///|
150
+ enum MyOption {
151
+ MyNone
152
+ MySome(Int)
153
+ } derive(Show, ToJson, Eq, Compare)
154
+
155
+ ///|
156
+ /// match + loops are expressions
157
+ test "everything is expression in MoonBit" {
158
+ // tuple
159
+ let (n, opt) = (1, MySome(2))
160
+ // if expressions return values
161
+ let msg : String = if n > 0 { "pos" } else { "non-pos" }
162
+ let res = match opt {
163
+ MySome(x) => {
164
+ inspect(x, content="2")
165
+ 1
166
+ }
167
+ MyNone => 0
168
+ }
169
+ let status : Result[Int, String] = Ok(10)
170
+ // match expressions return values
171
+ let description = match status {
172
+ Ok(value) => "Success: \{value}"
173
+ Err(error) => "Error: \{error}"
174
+ }
175
+ let array = [1, 2, 3, 4, 5]
176
+ let mut i = 0 // mutable bindings (local only, globals are immutable)
177
+ let target = 3
178
+ // loops return values with 'break'
179
+ let found : Int? = while i < array.length() {
180
+ if array[i] == target {
181
+ break Some(i) // Exit with value
182
+ }
183
+ i = i + 1
184
+ } else { // Value when loop completes normally
185
+ None
186
+ }
187
+ assert_eq(found, Some(2)) // Found at index 2
188
+ }
189
+
190
+ ///|
191
+ /// global bindings
192
+ pub let my_name : String = "MoonBit"
193
+
194
+ ///|
195
+ pub const PI : Double = 3.14159 // constants use UPPER_SNAKE or PascalCase
196
+
197
+ ///|
198
+ pub fn maximum(xs : Array[Int]) -> Int raise {
199
+ // Toplevel functions are *mutually recursive* by default
200
+ // `raise` annotation means the function would raise any Error
201
+ // Only add `raise XXError` when you do need track the specific error type
202
+ match xs {
203
+ [] => fail("Empty array") // fail() is built-in for generic errors
204
+ [x] => x
205
+ // pattern match over array, the `.. rest` is a rest pattern
206
+ // it is of type `ArrayView[Int]` which is a slice
207
+ [x, .. rest] => {
208
+ let mut max_val = x // `mut` only allowed in local bindings
209
+ for y in rest {
210
+ if y > max_val {
211
+ max_val = y
212
+ }
213
+ }
214
+ max_val // return can be omitted if the last expression is the return value
215
+ }
216
+ }
217
+ }
218
+
219
+ ///|
220
+ /// pub(all) means it can be both read and created outside the package
221
+ pub(all) struct Point {
222
+ x : Int
223
+ mut y : Int
224
+ } derive(Show, ToJson)
225
+
226
+ ///|
227
+ pub enum MyResult[T, E] {
228
+ MyOk(T) // semicolon `;` is optional when we have a newline
229
+ MyErr(E) // Enum variants must start uppercase
230
+ } derive(Show, Eq, ToJson)
231
+ // pub means it can only be pattern matched outside the package
232
+ // but it can not be created outside the package, use `pub(all)` otherwise
233
+
234
+ ///|
235
+ /// pub (open) means the trait can be implemented for outside packages
236
+ pub(open) trait Comparable {
237
+ compare(Self, Self) -> Int // `Self` refers to the implementing type
238
+ }
239
+
240
+ ///|
241
+ test "inspect test" {
242
+ let result = sum(1, 2)
243
+ inspect(result, content="3")
244
+ // The `content` can be auto-corrected by running `moon test --update`
245
+ let point = Point::{ x: 10, y: 20 }
246
+ // For complex structures, use @json.inspect for better readability:
247
+ @json.inspect(point, content={ "x": 10, "y": 20 })
248
+ }
249
+ ```
250
+
251
+ ## Integers, Char
252
+
253
+ MoonBit supports Byte, Int16, Int, UInt16, UInt, Int64, UInt64, etc. When the type is known,
254
+ the literal can be overloaded:
255
+
256
+ ```mbt check
257
+ test "integer and char literal overloading disambiguation via type in the current context" {
258
+ let a0 = 1 // a is Int by default
259
+ let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
260
+ 1, 1, 1, 1, 1,
261
+ )
262
+ assert_eq(int, uint16.to_int())
263
+ let a1 : Int = 'b' // this also works, a5 will be the unicode value
264
+ let a2 : Char = 'b'
265
+ }
266
+ ```
267
+
268
+ ## Bytes
269
+
270
+ Bytes is immutable; Indexing (`b[i]`) returns a `Byte`.
271
+
272
+ ```mbt check
273
+ test "bytes literals overloading and indexing" {
274
+ let b0 : Bytes = b"abcd"
275
+ let b1 : Bytes = "abcd" // b" prefix is optional, when we know the type
276
+ let b2 : Bytes = [0xff, 0x00, 0x01] // Array literal overloading
277
+ assert_eq(b0[0], b'a') // indexing returns Byte
278
+ }
279
+ ```
280
+
281
+ ## Array
282
+
283
+ MoonBit Array is resizable array, FixedArray is fixed size array.
284
+
285
+ ```mbt check
286
+ ///|
287
+ test "array literals overloading: disambiguation via type in the current context" {
288
+ let a0 : Array[Int] = [1, 2, 3] // resizable
289
+ let a1 : FixedArray[Int] = [1, 2, 3]
290
+ let a2 : ReadOnlyArray[Int] = [1, 2, 3]
291
+ let a3 : ArrayView[Int] = [1, 2, 3]
292
+
293
+ }
294
+ ```
295
+
296
+ ## String
297
+
298
+ MoonBit's String is immutable utf16 encoded, `s[i]` returns a code unit (UInt16),
299
+ `s.get_char(i)` returns `Option[Char]`.
300
+ Since MoonBit supports char literal overloading, you can write code snippets like this:
301
+
302
+ ```mbt check
303
+ #warnings("-unused_value")
304
+ test "string indexing and utf8 encode/decode" {
305
+ let s = "hello world"
306
+ let b0 : UInt16 = s[0]
307
+ assert_true(b0 is ('\n' | 'h' | 'b' | 'a'..='z'))
308
+ // In check mode (expression with explicit type), ('\n' : Int) is valid.
309
+ // Here the compiler knows `s[i]` is Int
310
+
311
+ // Using get_char for Option handling
312
+ let b1 : Char? = s.get_char(0)
313
+ assert_true(b1 is Some('a'..='z'))
314
+
315
+ // ⚠️ Important: Variables won't work with direct indexing
316
+ let eq_char : Char = '='
317
+ // s[0] == eq_char // ❌ Won't compile - eq_char is not a literal, lhs is UInt while rhs is Char
318
+ // Use: s[0] == '=' or s.get_char(0) == Some(eq_char)
319
+ let bytes = @encoding/utf8.encode("中文") // utf8 encode package is in stdlib
320
+ assert_true(bytes is [0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87])
321
+ let s2 : String = @encoding/utf8.decode(bytes) // decode utf8 bytes back to String
322
+ assert_true(s2 is "中文")
323
+ }
324
+ ```
325
+
326
+ #### String Interpolation
327
+
328
+ MoonBit uses `\{}` for string interpolation:
329
+
330
+ ```mbt check
331
+ test "string interpolation basics" {
332
+ let point : Point = { x: 10, y: 20 }
333
+ let name : String = "Moon"
334
+ let config = { "cache": 123 }
335
+ let version = 1.0
336
+ let message = "Hello \{name} v\{version}" // "Hello Moon v1.0"
337
+ let desc = "Point at \{point}" // Uses point.to_string()
338
+ // Works with any type implementing Show
339
+
340
+ // ❌ Wrong - quotes inside interpolation not allowed:
341
+ // println(" - Checking if 'cache' section exists: \{config["cache"]}")
342
+
343
+ // ✅ Correct - extract to variable first:
344
+ let has_key = config["cache"] // `"` not allowed in interpolation
345
+ println(" - Checking if 'cache' section exists: \{has_key}")
346
+ }
347
+ ```
348
+
349
+ <Important> expressions inside `\{}` can only be basic expressions (no quotes, newlines, or nested interpolations). String literals are not allowed as it makes lexing too difficult.
350
+ </Important>
351
+
352
+ #### Multiple line strings
353
+
354
+ ```mbt check
355
+ ///|
356
+ test "multi-line string literals" {
357
+ let multi_line_string : String =
358
+ #|Hello
359
+ #|World
360
+ #|
361
+ inspect(
362
+ multi_line_string,
363
+ content=(
364
+ #|Hello
365
+ #|World
366
+ #|
367
+ ), // when multiple line string is passed as argument, `()` wrapper is required
368
+ )
369
+ }
370
+ ```
371
+
372
+ ## Map
373
+
374
+ A built-in `Map` type that preserves insertion order (like
375
+ JavaScript's Map):
376
+
377
+ ```mbt check
378
+ ///|
379
+ test "map literals and common operations" {
380
+ // Map literal syntax
381
+ let map : Map[String, Int] = { "a": 1, "b": 2, "c": 3 }
382
+
383
+ // Empty map
384
+ let empty : Map[String, Int] = {}
385
+
386
+ // From array of pairs
387
+ let from_pairs : Map[String, Int] = Map::from_array([("x", 1), ("y", 2)])
388
+
389
+ // Set/update value
390
+ map["new-key"] = 3
391
+ map["a"] = 10 // Updates existing key
392
+
393
+ // Get value - returns Option[T]
394
+ assert_eq(map.get("new-key"), Some(3))
395
+ assert_eq(map.get("missing"), None)
396
+
397
+ // Direct access (panics if key missing)
398
+ let value : Int = map["a"] // value = 10
399
+
400
+ // Iteration preserves insertion order
401
+ for k, v in map {
402
+ println("\{k}: \{v}") // Prints: a: 10, b: 2, c: 3, new-key: 3
403
+ }
404
+
405
+ // Other common operations
406
+ map.remove("b")
407
+ assert_eq(map.contains("b"), false)
408
+ assert_eq(map.length(), 3)
409
+ }
410
+ ```
411
+
412
+ ## View Types
413
+
414
+ **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.
415
+
416
+ - `String` → `StringView` via `s[:]` or `s[start:end]`
417
+ - `Bytes` → `BytesView` via `b[:]` or `b[start:end]`
418
+ - `Array[T]` → `ArrayView[T]` via `a[:]` or `a[start:end]`
419
+
420
+ **Important**: StringView slice is slightly different due to unicode safety:
421
+ `s[a:b]` may raise an error at surrogate boundaries (UTF-16 encoding edge case). You have two options:
422
+
423
+ - Use `try! s[a:b]` if you're certain the boundaries are valid (crashes on invalid boundaries)
424
+ - Let the error propagate to the caller for proper handling
425
+
426
+ **When to use views**:
427
+
428
+ - Pattern matching with rest patterns (`[first, .. rest]`)
429
+ - Passing slices to functions without allocation overhead
430
+ - Avoiding unnecessary copies of large sequences
431
+
432
+ Convert back with `.to_string()`, `.to_bytes()`, or `.to_array()` when you need ownership.
433
+
434
+ ## Complex Types
435
+
436
+ ```mbt check
437
+ ///|
438
+ type UserId = Int // Int is aliased to UserId - like symlink
439
+
440
+ ///|
441
+ /// Tuple-struct for callback
442
+ struct Handler((String) -> Unit) // A newtype wrapper
443
+
444
+ ///|
445
+ /// Tuple-struct syntax for single-field newtypes
446
+ struct Meters(Int) // Tuple-struct syntax
447
+
448
+ ///|
449
+ let distance : Meters = Meters(100)
450
+
451
+ ///|
452
+ let raw : Int = distance.0 // Access first field with .0
453
+
454
+ ///|
455
+ struct Addr {
456
+ host : String
457
+ port : Int
458
+ } derive(Show, Eq, ToJson, FromJson)
459
+
460
+ ///|
461
+ /// Structural types with literal syntax
462
+ let config : Addr = {
463
+ // `Type::` can be omitted if the type is already known
464
+ // otherwise `Type::{...}`
465
+ host: "localhost",
466
+ port: 8080,
467
+ }
468
+
469
+ ///|
470
+ /// Recursive enum for trees
471
+ enum Tree[T] {
472
+ Leaf(T)
473
+ Node(left~ : Tree[T], T, right~ : Tree[T]) // enum can use labels
474
+ }
475
+
476
+ // Pattern match on enum variants
477
+
478
+ ///|
479
+ fn sum_tree(tree : Tree[Int]) -> Int {
480
+ match tree {
481
+ Leaf(x) => x
482
+ Node(left~, x, right~) => sum_tree(left) + x + sum_tree(right)
483
+ }
484
+ }
485
+ ```
486
+
487
+ ## Common Derivable Traits
488
+
489
+ Most types can automatically derive standard traits using the `derive(...)` syntax:
490
+
491
+ - **`Show`** - Enables `to_string()` and string interpolation with `\{value}`
492
+ - **`Eq`** - Enables `==` and `!=` equality operators
493
+ - **`Compare`** - Enables `<`, `>`, `<=`, `>=` comparison operators
494
+ - **`ToJson`** - Enables `@json.inspect()` for readable test output
495
+ - **`Hash`** - Enables use as Map keys
496
+
497
+ ```mbt check
498
+ ///|
499
+ struct Coordinate {
500
+ x : Int
501
+ y : Int
502
+ } derive(Show, Eq, ToJson)
503
+
504
+ ///|
505
+ enum Status {
506
+ Active
507
+ Inactive
508
+ } derive(Show, Eq, Compare)
509
+ ```
510
+
511
+ **Best practice**: Always derive `Show` and `Eq` for data types. Add `ToJson` if you plan to test them with `@json.inspect()`.
512
+
513
+ ## Reference Semantics by Default
514
+
515
+ MoonBit passes most types by reference semantically (the optimizer may copy
516
+ immutables):
517
+
518
+ ```mbt check
519
+ ///|
520
+ /// Structs with 'mut' fields are always passed by reference
521
+ struct Counter {
522
+ mut value : Int
523
+ }
524
+
525
+ ///|
526
+ fn increment(c : Counter) -> Unit {
527
+ c.value += 1 // Modifies the original
528
+ }
529
+
530
+ ///|
531
+ /// Arrays and Maps are mutable references
532
+ fn modify_array(arr : Array[Int]) -> Unit {
533
+ arr[0] = 999 // Modifies original array
534
+ }
535
+
536
+ ///|
537
+ /// Use Ref[T] for explicit mutable references to primitives
538
+ fn swap_values(a : Ref[Int], b : Ref[Int]) -> Unit {
539
+ let temp = a.val
540
+ a.val = b.val
541
+ b.val = temp
542
+ }
543
+
544
+ ///|
545
+ test "ref swap" {
546
+ let x : Ref[Int] = Ref::new(10)
547
+ let y : Ref[Int] = Ref::new(20)
548
+ swap_values(x, y) // x.val is now 20, y.val is now 10
549
+ }
550
+ ```
551
+
552
+ ## Pattern Matching
553
+
554
+ MoonBit's pattern matching is comprehensive and exhaustive:
555
+
556
+ ```mbt check
557
+ ///|
558
+ /// Destructure arrays with rest patterns
559
+ fn process_array(arr : Array[Int]) -> String {
560
+ match arr {
561
+ [] => "empty"
562
+ [single] => "one: \{single}"
563
+ [first, .. _middle, last] => "first: \{first}, last: \{last}"
564
+ // middle is of type ArrayView[Int]
565
+ }
566
+ }
567
+
568
+ ///|
569
+ fn analyze_point(point : Point) -> String {
570
+ match point {
571
+ { x: 0, y: 0 } => "origin"
572
+ { x, y } if x == y => "on diagonal"
573
+ { x, .. } if x < 0 => "left side"
574
+ _ => "other"
575
+ }
576
+ }
577
+
578
+ ///|
579
+ /// StringView pattern matching for parsing
580
+ fn is_palindrome(s : StringView) -> Bool {
581
+ loop s {
582
+ [] | [_] => true
583
+ [a, .. rest, b] if a == b => continue rest
584
+ // a is of type Char, rest is of type StringView
585
+ _ => false
586
+ }
587
+ }
588
+ ```
589
+
590
+ ## Functional `loop` control flow
591
+
592
+ The `loop` construct is unique to MoonBit:
593
+
594
+ ```mbt check
595
+ ///|
596
+ /// Functional loop with pattern matching on loop variables
597
+ /// @list.List is from the standard library
598
+ fn sum_list(list : @list.List[Int]) -> Int {
599
+ loop (list, 0) {
600
+ (Empty, acc) => acc // Base case returns accumulator
601
+ (More(x, tail=rest), acc) => continue (rest, x + acc) // Recurse with new values
602
+ }
603
+ }
604
+
605
+ ///|
606
+ /// Multiple loop variables with complex control flow
607
+ fn find_pair(arr : Array[Int], target : Int) -> (Int, Int)? {
608
+ loop (0, arr.length() - 1) {
609
+ (i, j) if i >= j => None
610
+ (i, j) => {
611
+ let sum = arr[i] + arr[j]
612
+ if sum == target {
613
+ Some((i, j)) // Found pair
614
+ } else if sum < target {
615
+ continue (i + 1, j) // Move left pointer
616
+ } else {
617
+ continue (i, j - 1) // Move right pointer
618
+ }
619
+ }
620
+ }
621
+ }
622
+ ```
623
+
624
+ **Note**: You must provide a payload to `loop`. If you want an infinite loop, use `while true { ... }` instead. The syntax `loop { ... }` without arguments is invalid.
625
+
626
+ ## Functional `for` control flow
627
+
628
+ `for` loops have unique MoonBit features:
629
+
630
+ ```mbt check
631
+ ///|
632
+ test "functional for loop control flow" {
633
+ // For loop with multiple loop variables,
634
+ // i and j are loop state
635
+ let sum_result : Int = for i = 0, sum = 0 {
636
+ if i <= 10 {
637
+ continue i + 1, sum + i
638
+ // update new loop state in a functional way
639
+ } else { // Continue with new values
640
+ break sum // Final value when loop completes normally
641
+ }
642
+ }
643
+ inspect(sum_result, content="55")
644
+
645
+ // special form with condition and state update in the `for` header
646
+ let sum_result2 : Int = for i = 0, sum = 0; i <= 10; i = i + 1, sum = sum + i {
647
+
648
+ } else {
649
+ sum
650
+ }
651
+ inspect(sum_result2, content="55")
652
+ }
653
+ ```
654
+
655
+ ## Label and Optional Parameters
656
+
657
+ Good example: Use labeled and optional parameters
658
+
659
+ ```mbt
660
+ fn g(
661
+ positional : Int,
662
+ required~ : Int,
663
+ optional? : Int,
664
+ optional_with_default? : Int = 42,
665
+ ) -> String {
666
+ // make sure you understand the types of the arguments really is:
667
+ let _ : Int = positional
668
+ let _ : Int = required
669
+ // let _ : Option[Int] = optional
670
+ let _ : Int = optional_with_default
671
+ "\{positional},\{required},\{optional},\{optional_with_default}"
672
+ }
673
+
674
+ test {
675
+ inspect(g(1, required=2), content="1,2,None,42")
676
+ inspect(g(1, required=2, optional=3), content="1,2,Some(3),42")
677
+ inspect(g(1, required=4, optional_with_default=100), content="1,4,None,100")
678
+ }
679
+ ```
680
+
681
+ Misuse: `arg : Type?` is not an optional parameter
682
+
683
+ ```mbt
684
+ fn with_config(a : Int?, b : Int?, c : Int) -> String {
685
+ // T? is syntactic sugar for Option[T]
686
+ "\{a},\{b},\{c}"
687
+ }
688
+
689
+ test {
690
+ inspect(with_config(None, None, 1), content="None,None,1")
691
+ inspect(with_config(Some(5), Some(5), 1), content="Some(5),Some(5),1")
692
+ }
693
+ ```
694
+
695
+ Anti pattern: `arg? : Type?`
696
+
697
+ ```mbt
698
+ // How to fix: declare `(a? : Int, b? : Int = 1)` directly
699
+ fn f(a? : Int?, b? : Int? = Some(1)) -> Unit {...}
700
+ test {
701
+ // How to fix: call `f(b=2)` directly
702
+ f(a=None, b=Some(2))
703
+ }
704
+ ```
705
+
706
+ Bad example: `arg : APIOptions`
707
+
708
+ ```mbt
709
+ // Do not use struct to group options.
710
+ struct APIOptions {
711
+ a : Int?
712
+ }
713
+
714
+ fn not_idiomatic(opts : APIOptions, arg : Int) -> Unit {
715
+ ...
716
+ }
717
+
718
+ test {
719
+ // Hard to use in call site
720
+ not_idiomatic({ a: Some(5) }, 10)
721
+ not_idiomatic({ a: None }, 10)
722
+ }
723
+ ```
724
+
725
+ ## Checked Errors
726
+
727
+ MoonBit uses **checked** error-throwing functions, not unchecked exceptions,
728
+ it is recommended to use `raise` for functions and use `Result` in testing.
729
+
730
+ ```mbt check
731
+ ///|
732
+ /// Declare error types with 'suberror'
733
+ suberror ValueError String
734
+
735
+ ///|
736
+ struct Position(Int, Int) derive(ToJson, Show, Eq)
737
+
738
+ ///|
739
+ pub(all) suberror ParseError {
740
+ InvalidChar(Position, Char)
741
+ InvalidEof
742
+ InvalidNumber(Position, String)
743
+ InvalidIdentEscape(Position)
744
+ } derive(Eq, ToJson, Show)
745
+
746
+ ///|
747
+ /// Functions declare what they can throw
748
+ fn parse_int(s : String) -> Int raise ParseError {
749
+ // 'raise' throws an error
750
+ if s.is_empty() {
751
+ raise ParseError::InvalidEof
752
+ }
753
+ ... // parsing logic
754
+ }
755
+
756
+ ///|
757
+ fn div(x : Int, y : Int) -> Int raise {
758
+ if y == 0 {
759
+ raise Failure("Division by zero")
760
+ }
761
+ x / y
762
+ }
763
+
764
+ ///|
765
+ test "inspect raise function" {
766
+ inspect(
767
+ try? div(1, 0),
768
+ content=(
769
+ #|Err(Failure("Division by zero"))
770
+ ),
771
+ ) // Result[Int, MyError]
772
+ }
773
+
774
+ // Three ways to handle errors:
775
+
776
+ ///|
777
+ /// Propagate automatically
778
+ fn use_parse() -> Int raise ParseError {
779
+ let x = parse_int("123")
780
+ // Error *auto* propagates by default.
781
+ // *unlike* Swift, you don't need mark `try` for functions that can raise errors,
782
+ // compiler infers it automatically. This makes error-handling code cleaner
783
+ // while still being type-safe and explicit about what errors can occur.
784
+ x * 2
785
+ }
786
+
787
+ ///|
788
+ /// Mark `raise` for all possible errors, don't care what error it is
789
+ /// If you are doing a quick prototype, just mark it as raise is good enough.
790
+ fn use_parse2() -> Int raise {
791
+ let x = parse_int("123")
792
+ x * 2
793
+ }
794
+
795
+ ///|
796
+ /// Convert to Result with try?
797
+ fn safe_parse(s : String) -> Result[Int, ParseError] {
798
+ let val1 : Result[_] = try? parse_int(s) // Returns Result[Int, ParseError]
799
+ // try! is rarely used - it panics on error, similar to unwrap() in Rust
800
+ // let val2 : Int = try! parse_int(s) // Returns Int otherwise crash
801
+
802
+ // Alternative explicit handling:
803
+ let val3 = try parse_int(s) catch {
804
+ err => Err(err)
805
+ } noraise { // noraise block is optional - handles the success case
806
+ v => Ok(v)
807
+ }
808
+ ...
809
+ }
810
+
811
+ ///|
812
+ /// 3. Handle with try-catch
813
+ fn handle_parse(s : String) -> Int {
814
+ parse_int(s) catch {
815
+ ParseError::InvalidEof => {
816
+ println("Parse failed: InvalidEof")
817
+ -1 // Default value
818
+ }
819
+ _ => 2
820
+ }
821
+ }
822
+ ```
823
+
824
+ # Methods and Traits
825
+
826
+ Methods use `Type::method_name` syntax, traits require explicit implementation:
827
+
828
+ ```mbt check
829
+ ///|
830
+ struct Rectangle {
831
+ width : Double
832
+ height : Double
833
+ }
834
+
835
+ ///|
836
+ // Methods are prefixed with Type::
837
+ fn Rectangle::area(self : Rectangle) -> Double {
838
+ self.width * self.height
839
+ }
840
+
841
+ ///|
842
+ /// Static methods don't need self
843
+ fn Rectangle::new(w : Double, h : Double) -> Rectangle {
844
+ { width: w, height: h }
845
+ }
846
+
847
+ ///|
848
+ /// Show trait now uses output(self, logger) for custom formatting
849
+ /// to_string() is automatically derived from this
850
+ pub impl Show for Rectangle with output(self, logger) {
851
+ logger.write_string("Rectangle(\{self.width}x\{self.height})")
852
+ }
853
+
854
+ ///|
855
+ /// Traits can have non-object-safe methods
856
+ trait Named {
857
+ name() -> String // No 'self' parameter - not object-safe
858
+ }
859
+
860
+ ///|
861
+ /// Trait bounds in generics
862
+ fn[T : Show + Named] describe(value : T) -> String {
863
+ "\{T::name()}: \{value.to_string()}"
864
+ }
865
+
866
+ ///|
867
+ /// Trait implementation
868
+ impl Hash for Rectangle with hash_combine(self, hasher) {
869
+ hasher..combine(self.width)..combine(self.height)
870
+ }
871
+ ```
872
+
873
+ ## Operator Overloading
874
+
875
+ MoonBit supports operator overloading through traits:
876
+
877
+ ```mbt check
878
+ ///|
879
+ struct Vector(Int, Int)
880
+
881
+ ///|
882
+ /// Implement arithmetic operators
883
+ pub impl Add for Vector with add(self, other) {
884
+ Vector(self.0 + other.0, self.1 + other.1)
885
+ }
886
+
887
+ ///|
888
+ pub impl Mul for Vector with mul(self, other) {
889
+ Vector(self.0 * other.0, self.1 * other.1)
890
+ }
891
+
892
+ ///|
893
+ struct Person {
894
+ age : Int
895
+ } derive(Eq)
896
+
897
+ ///|
898
+ /// Comparison operators
899
+ pub impl Compare for Person with compare(self, other) {
900
+ self.age.compare(other.age)
901
+ }
902
+
903
+ ///|
904
+ test "overloading" {
905
+ let v1 : Vector = Vector(1, 2)
906
+ let v2 : Vector = Vector(3, 4)
907
+ let _v3 : Vector = v1 + v2
908
+
909
+ }
910
+ ```
911
+
912
+ ## Access Control Modifiers
913
+
914
+ MoonBit has fine-grained visibility control:
915
+
916
+ ```mbt check
917
+ ///|
918
+ /// `fn` defaults to Private - only visible in current package
919
+ fn internal_helper() -> Unit {
920
+ ...
921
+ }
922
+
923
+ ///|
924
+ pub fn get_value() -> Int {
925
+ ...
926
+ }
927
+
928
+ ///|
929
+ // Struct (default) - type visible, implementation hidden
930
+ struct DataStructure {}
931
+
932
+ ///|
933
+ /// `pub struct` defaults to readonly - can read, pattern match, but not create
934
+ pub struct Config {}
935
+
936
+ ///|
937
+ /// Public all - full access
938
+ pub(all) struct Config2 {}
939
+
940
+ ///|
941
+ /// Abstract trait (default) - cannot be implemented by
942
+ /// types outside this package
943
+ pub trait MyTrait {}
944
+
945
+ ///|
946
+ /// Open for extension
947
+ pub(open) trait Extendable {}
948
+ ```
949
+
950
+ # Best Practices and Reference
951
+
952
+ ## Common Pitfalls to Avoid
953
+
954
+ 1. **Don't use uppercase for variables/functions** - compilation error
955
+ 2. **Don't forget `mut` for mutable fields** - immutable by default
956
+ 3. **Don't assume value semantics** - most types pass by reference
957
+ 4. **Don't ignore error handling** - errors must be explicitly handled
958
+ 5. **Don't use `return` unnecessarily** - last expression is the return value
959
+ 6. **Don't create methods without Type:: prefix** - methods need explicit type prefix
960
+ 7. Don't forget to handle array bounds - use get() for safe access
961
+ 8. Don't mix up String indexing (returns Int). Use `for char in s {...}` for char iteration
962
+ 9. Don't forget @package prefix when calling functions from other packages
963
+ 10. Don't use ++ or -- (not supported), use `i = i + 1` or `i += 1`
964
+ 11. **Don't add explicit `try` for error-raising functions** - errors propagate automatically (unlike Swift)
965
+ 12. **Legacy syntax**: Older code may use `function_name!(...)` or `function_name(...)?` - these are deprecated; use normal calls and `try?` for Result conversion
966
+
967
+ # MoonBit Build System - Essential Guide
968
+
969
+ ## Idiomatic Project Structure
970
+
971
+ MoonBit projects use `moon.mod.json` (module descriptor) and `moon.pkg.json`
972
+ (package descriptor):
973
+
974
+ ```
975
+ my_module
976
+ ├── Agents.md # Guide to Agents
977
+ ├── README.mbt.md # Markdown with tested code blocks (`test "..." { ... }`)
978
+ ├── README.md -> README.mbt.md
979
+ ├── cmd # Command line directory
980
+ │ └── main
981
+ │ ├── main.mbt
982
+ │ └── moon.pkg.json # executable package with {"is_main": true}
983
+ ├── liba/ # Library packages
984
+ │ └── moon.pkg.json # Referenced by other packages as `@username/my_module/liba`
985
+ │ └── libb/ # Library packages
986
+ │ └── moon.pkg.json # Referenced by other packages as `@username/my_module/liba/libb`
987
+ ├── moon.mod.json # Module metadata, source field(optional) specifies the source directory of the module
988
+ ├── moon.pkg.json # Package metadata (each directory is a package like Golang)
989
+ ├── user_pkg.mbt # Root packages, referenced by other packages as `@username/my_module`
990
+ ├── user_pkg_wbtest.mbt # White-box tests (only needed for testing internal private members, similar to Golang's package mypackage)
991
+ └── user_pkg_test.mbt # Black-box tests
992
+ └── ... # More package files, symbols visible to current package (like Golang)
993
+ ```
994
+
995
+ ## Essential Commands
996
+
997
+ - `moon new my_project` - Create new project
998
+ - `moon run cmd/main` - Run main package
999
+ - `moon build` - Build project
1000
+ - `moon check` - Type check without building, use it regularly
1001
+ - `moon check --target all` - Type check for all backends
1002
+ - `moon add package` - Add dependency
1003
+ - `moon remove package` - Remove dependency
1004
+ - `moon fmt` - Format code
1005
+
1006
+ ### Test Commands
1007
+
1008
+ - `moon test` - Run all tests
1009
+ - `moon test --update`
1010
+ - `moon test -v` - Verbose output with test names
1011
+ - `moon test dirname` - Test specific directory
1012
+ - `moon test filename` - Test specific file in a directory
1013
+ - `moon coverage analyze` - Analyze coverage
1014
+
1015
+ ## Package Management
1016
+
1017
+ ### Adding Dependencies
1018
+
1019
+ ```bash
1020
+ moon add moonbitlang/x # Add latest version
1021
+ moon add moonbitlang/x@0.4.6 # Add specific version
1022
+ ```
1023
+
1024
+ ### Updating Dependencies
1025
+
1026
+ ```bash
1027
+ moon update # Update package index
1028
+ ```
1029
+
1030
+ ## Key Configuration
1031
+
1032
+ ### Module (`moon.mod.json`)
1033
+
1034
+ ```json
1035
+ {
1036
+ "name": "username/hello", // Required format for published modules
1037
+ "version": "0.1.0",
1038
+ "source": ".", // Source directory(optional, default: ".")
1039
+ "repository": "", // Git repository URL
1040
+ "keywords": [], // Search keywords
1041
+ "description": "...", // Module description
1042
+ "deps": {
1043
+ // Dependencies from mooncakes.io, using`moon add` to add dependencies
1044
+ "moonbitlang/x": "0.4.6"
1045
+ }
1046
+ }
1047
+ ```
1048
+
1049
+ ### Package (`moon.pkg.json`)
1050
+
1051
+ ```json
1052
+ {
1053
+ "is_main": true, // Creates executable when true
1054
+ "import": [ // Package dependencies
1055
+ "username/hello/liba", // Simple import, use @liba.foo() to call functions
1056
+ {
1057
+ "path": "moonbitlang/x/encoding",
1058
+ "alias": "libb" // Custom alias, use @libb.encode() to call functions
1059
+ }
1060
+ ],
1061
+ "test-import": [...], // Imports for black-box tests, similar to import
1062
+ "wbtest-import": [...] // Imports for white-box tests, similar to import (rarely used)
1063
+ }
1064
+ ```
1065
+
1066
+ Packages per directory, packages without `moon.pkg.json` are not recognized.
1067
+
1068
+ ## Package Importing (used in moon.pkg.json)
1069
+
1070
+ - **Import format**: `"module_name/package_path"`
1071
+ - **Usage**: `@alias.function()` to call imported functions
1072
+ - **Default alias**: Last part of path (e.g., `liba` for `username/hello/liba`)
1073
+ - **Package reference**: Use `@packagename` in test files to reference the
1074
+ tested package
1075
+
1076
+ **Package Alias Rules**:
1077
+
1078
+ - Import `"username/hello/liba"` → use `@liba.function()` (default alias is last path segment)
1079
+ - Import with custom alias `{"path": "moonbitlang/x/encoding", "alias": "enc"}` → use `@enc.function()`
1080
+ - In `_test.mbt` or `_wbtest.mbt` files, the package being tested is auto-imported
1081
+
1082
+ Example:
1083
+
1084
+ ```mbt
1085
+ ///|
1086
+ /// In main.mbt after importing "username/hello/liba" in `moon.pkg.json`
1087
+ fn main {
1088
+ println(@liba.hello()) // Calls hello() from liba package
1089
+ }
1090
+ ```
1091
+
1092
+ ## Using Standard Library (moonbitlang/core)
1093
+
1094
+ **MoonBit standard library (moonbitlang/core) packages are automatically imported** - DO NOT add them to dependencies:
1095
+
1096
+ - ❌ **DO NOT** use `moon add` to add standard library packages like `moonbitlang/core/strconv`
1097
+ - ❌ **DO NOT** add standard library packages to `"deps"` field of `moon.mod.json`
1098
+ - ❌ **DO NOT** add standard library packages to `"import"` field of `moon.pkg.json`
1099
+ - ✅ **DO** use them directly: `@strconv.parse_int()`, `@list.List`, `@array.fold()`, etc.
1100
+
1101
+ If you get an error like "cannot import `moonbitlang/core/strconv`", remove it from imports - it's automatically available.
1102
+
1103
+ ## Creating Packages
1104
+
1105
+ To add a new package `fib` under `.`:
1106
+
1107
+ 1. Create directory: `./fib/`
1108
+ 2. Add `./fib/moon.pkg.json`: `{}` -- Minimal valid moon.pkg.json
1109
+ 3. Add `.mbt` files with your code
1110
+ 4. Import in dependent packages:
1111
+
1112
+ ```json
1113
+ {
1114
+ "import": [
1115
+ "username/hello/fib",
1116
+ ...
1117
+ ]
1118
+ }
1119
+ ```
1120
+
1121
+ ## Conditional Compilation
1122
+
1123
+ Target specific backends/modes in `moon.pkg.json`:
1124
+
1125
+ ```json
1126
+ {
1127
+ "targets": {
1128
+ "wasm_only.mbt": ["wasm"],
1129
+ "js_only.mbt": ["js"],
1130
+ "debug_only.mbt": ["debug"],
1131
+ "wasm_or_js.mbt": ["wasm", "js"], // for wasm or js backend
1132
+ "not_js.mbt": ["not", "js"], // for nonjs backend
1133
+ "complex.mbt": ["or", ["and", "wasm", "release"], ["and", "js", "debug"]] // more complex conditions
1134
+ }
1135
+ }
1136
+ ```
1137
+
1138
+ **Available conditions:**
1139
+
1140
+ - **Backends**: `"wasm"`, `"wasm-gc"`, `"js"`, `"native"`
1141
+ - **Build modes**: `"debug"`, `"release"`
1142
+ - **Logical operators**: `"and"`, `"or"`, `"not"`
1143
+
1144
+ ## Link Configuration
1145
+
1146
+ ### Basic Linking
1147
+
1148
+ ```json
1149
+ {
1150
+ "link": true, // Enable linking for this package
1151
+ // OR for advanced cases:
1152
+ "link": {
1153
+ "wasm": {
1154
+ "exports": ["hello", "foo:bar"], // Export functions
1155
+ "heap-start-address": 1024, // Memory layout
1156
+ "import-memory": {
1157
+ // Import external memory
1158
+ "module": "env",
1159
+ "name": "memory"
1160
+ },
1161
+ "export-memory-name": "memory" // Export memory with name
1162
+ },
1163
+ "wasm-gc": {
1164
+ "exports": ["hello"],
1165
+ "use-js-builtin-string": true, // JS String Builtin support
1166
+ "imported-string-constants": "_" // String namespace
1167
+ },
1168
+ "js": {
1169
+ "exports": ["hello"],
1170
+ "format": "esm" // "esm", "cjs", or "iife"
1171
+ },
1172
+ "native": {
1173
+ "cc": "gcc", // C compiler
1174
+ "cc-flags": "-O2 -DMOONBIT", // Compile flags
1175
+ "cc-link-flags": "-s" // Link flags
1176
+ }
1177
+ }
1178
+ }
1179
+ ```
1180
+
1181
+ ## Warning Control
1182
+
1183
+ Disable specific warnings in `moon.mod.json` or `moon.pkg.json`:
1184
+
1185
+ ```json
1186
+ {
1187
+ "warn-list": "-2-29" // Disable unused variable (2) & unused package (29)
1188
+ }
1189
+ ```
1190
+
1191
+ **Common warning numbers:**
1192
+
1193
+ - `1` - Unused function
1194
+ - `2` - Unused variable
1195
+ - `11` - Partial pattern matching
1196
+ - `12` - Unreachable code
1197
+ - `29` - Unused package
1198
+
1199
+ Use `moonc build-package -warn-help` to see all available warnings.
1200
+
1201
+ ## Pre-build Commands
1202
+
1203
+ Embed external files as MoonBit code:
1204
+
1205
+ ```json
1206
+ {
1207
+ "pre-build": [
1208
+ {
1209
+ "input": "data.txt",
1210
+ "output": "embedded.mbt",
1211
+ "command": ":embed -i $input -o $output --name data --text"
1212
+ },
1213
+ ... // more embed commands
1214
+ ]
1215
+ }
1216
+ ```
1217
+
1218
+ Generated code example:
1219
+
1220
+ ```mbt check
1221
+ ///|
1222
+ let data : String =
1223
+ #|hello,
1224
+ #|world
1225
+ #|
1226
+ ```
1227
+
1228
+ # Documentation
1229
+
1230
+ Write documentation using `///` comments (started with `///|` to delimit the
1231
+ block code)
1232
+
1233
+ ````mbt check
1234
+ ///|
1235
+ /// Get the largest element of a non-empty `Array`.
1236
+ ///
1237
+ /// # Example
1238
+ /// ```mbt check
1239
+ /// test {
1240
+ /// inspect(sum_array([1, 2, 3, 4, 5, 6]), content="21")
1241
+ /// }
1242
+ /// ```
1243
+ ///
1244
+ /// # Panics
1245
+ /// Panics if the `xs` is empty.
1246
+ pub fn sum_array(xs : Array[Int]) -> Int {
1247
+ xs.fold(init=0, (a, b) => a + b)
1248
+ }
1249
+ ````
1250
+
1251
+ The MoonBit code in docstring will be type checked and tested automatically.
1252
+ (using `moon test --update`)
1253
+
1254
+ # Development Workflow
1255
+
1256
+ ## MoonBit Tips
1257
+
1258
+ - MoonBit code is organized in files/block style.
1259
+ A package is composed of a list of files, their order does not matter,
1260
+ keep them separate so that it is easy to focus on critical parts.
1261
+
1262
+ Each block is separated by `///|`, the order of each block is irrelevant too. You can process
1263
+ block by block independently.
1264
+
1265
+ You are encouraged to generate code in a block-by-block manner.
1266
+
1267
+ You are encouraged to search and replace block by block instead of
1268
+ replacing the whole file.
1269
+
1270
+ You are encouraged to keep each file focused.
1271
+
1272
+ - SPLIT the large file into small files, the order does not matter.
1273
+
1274
+ - Try to keep deprecated blocks in file called `deprecated.mbt` in each
1275
+ directory.
1276
+
1277
+ - `moon fmt` is used to format your code properly.
1278
+
1279
+ - `moon info` is used to update the generated interface of the package
1280
+ **in current project**. Each package has a generated interface file `.mbti`,
1281
+ it is a brief formal description of the package. If nothing in `.mbti`
1282
+ changes, this means your change does not bring the visible changes to the
1283
+ external package users, it is typically a safe refactoring.
1284
+ **Note**: `moon info` will only work with packages in the current project, and
1285
+ therefore you cannot use `moon info` to generate interface for dependencies
1286
+ like standard library.
1287
+
1288
+ - So in the last step, you typically run `moon info && moon fmt` to update the
1289
+ interface and format the code. You also check the diffs of `.mbti` file to see
1290
+ if the changes are expected.
1291
+
1292
+ - You should run `moon test` to check the test is passed. MoonBit supports
1293
+ snapshot testing, so in some cases, your changes indeed change the behavior of
1294
+ the code, you should run `moon test --update` to update the snapshot.
1295
+
1296
+ - You can run `moon check` to check the code is linted correctly, run it
1297
+ regularly to ensure you are not in a messy state.
1298
+
1299
+ - MoonBit packages are organized per directory; each directory has a
1300
+ `moon.pkg.json` listing its dependencies. Each package has its files and
1301
+ blackbox test files (common, ending in `_test.mbt`) and whitebox test files
1302
+ (ending in `_wbtest.mbt`).
1303
+
1304
+ - In the toplevel directory, there is a `moon.mod.json` file describing the
1305
+ module and metadata.
1306
+
1307
+ ## MoonBit Package `README` Generation Guide
1308
+
1309
+ - Output `README.mbt.md` in the package directory.
1310
+ `*.mbt.md` file and docstring contents treats `mbt check` specially.
1311
+ `mbt check` block will be included directly as code and also run by `moon check` and `moon test`.
1312
+ In docstrings, `mbt check` should only contain test blocks.
1313
+ If you are only referencing types from the package, you should use `mbt` which will only be syntax highlighted.
1314
+ Symlink `README.mbt.md` to `README.md` to adapt to systems that expect `README.md`.
1315
+ - Aim to cover ≥90% of the public API with concise sections and examples.
1316
+ - Organize by feature: construction, consumption, transformation, and key usage tips.
1317
+
1318
+ ## MoonBit Testing Guide
1319
+
1320
+ Practical testing guidance for MoonBit. Keep tests black-box by default and rely on snapshot `inspect(...)`.
1321
+
1322
+ - Black-box by default: Call only public APIs via `@package.fn`. Use white-box tests only when private members matter.
1323
+ - **Snapshots**: Prefer `inspect(value, content="...")`. If unknown, write `inspect(value)` and run `moon test --update` (or `moon test -u`).
1324
+ - Use regular `inspect()` for simple values (uses `Show` trait)
1325
+ - Use `@json.inspect()` for complex nested structures (uses `ToJson` trait, produces more readable output)
1326
+ - It is encouraged to `inspect` or `@json.inspect` the whole return value of a function if
1327
+ the whole return value is not huge, this makes test simple. You need `impl (Show|ToJson) for YourType` or `derive (Show, ToJson)`.
1328
+ - **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).
1329
+ - Grouping: Combine related checks in one `test "..." { ... }` block for speed and clarity.
1330
+ - Panics: Name test with prefix `test "panic ..." {...}`; if the call returns a value, wrap it with `ignore(...)` to silence warnings.
1331
+ - Errors: Use `try? f()` to get `Result[...]` and `inspect` it when a function may raise.
1332
+ - Verify: Run `moon test` (or `-u` to update snapshots) and `moon fmt` afterwards.
1333
+
1334
+ ## Spec-driven Development
1335
+
1336
+ - The spec can be written in a readonly `spec.mbt` file (name is conventional, not mandatory) with stub code marked as declarations:
1337
+
1338
+ ```mbt check
1339
+ #declaration_only
1340
+ pub type Yaml
1341
+ #declaration_only
1342
+ pub fn Yaml::to_string(y : Yaml) -> String raise {...}
1343
+ #declaration_only
1344
+ pub fn parse_yaml(s : String) -> Yaml raise {...}
1345
+ ```
1346
+
1347
+ - Add `spec_easy_test.mbt`, `spec_difficult_test.mbt` etc to test the spec functions; everything will be type-checked.
1348
+ - The AI or students can implement the `declaration_only` functions in different files thanks to our package organization.
1349
+ - Run `moon test` to check everything is correct.
1350
+
1351
+ - `#declaration_only` is supported for functions, methods, and types.
1352
+ - The `pub type Yaml` line is an intentionally opaque placeholder; the implementer chooses its representation.
1353
+ - Note the spec file can also contain normal code, not just declarations.
1354
+
1355
+ # Semantics based CLI tools
1356
+
1357
+ ## API Discovery with `moon doc`
1358
+
1359
+ **CRITICAL**: `moon doc '<query>'` is your PRIMARY tool for discovering available APIs, functions, types, and methods in MoonBit. It is **more powerful and accurate** than `grep_search`, `semantic_search`, or any file-based searching tools. Always prefer `moon doc` over other approaches when exploring what APIs are available.
1360
+
1361
+ ### Query Syntax
1362
+
1363
+ `moon doc` uses a specialized query syntax designed for symbol lookup:
1364
+
1365
+ - **Empty query**: `moon doc `
1366
+ - In a module: shows all available packages in current module
1367
+ - In a package: shows all symbols in current package
1368
+ - Outside package: shows all available packages
1369
+
1370
+ - **Function/value lookup**: `moon doc "[@pkg.]sym"`
1371
+ - **Type lookup**: `moon doc "[@pkg.]Sym"`
1372
+
1373
+ - **Method/field lookup**: `moon doc "[@pkg.]T::sym"`
1374
+
1375
+ - **Package exploration**: `moon doc "@pkg"`
1376
+ - Show package `pkg` and list all its exported symbols
1377
+ - Example: `moon doc "@json"` - explore entire `@json` package
1378
+ - Example: `moon doc "@encoding/utf8"` - explore nested package
1379
+
1380
+ ### Workflow for API Discovery
1381
+
1382
+ 1. **Finding functions**: Use `moon doc "@pkg.function_name"` before grep searching
1383
+ 2. **Exploring packages**: Use `moon doc "@pkg"` to see what's available in a package
1384
+ 3. **Method discovery**: Use `moon doc "Type::method"` to find methods on types
1385
+ 4. **Type inspection**: Use `moon doc "TypeName"` to see type definition and methods
1386
+ 5. **Package exploration**: Use `moon doc ""` at module root to see all available packages, including dependencies and stdlib
1387
+ 6. **Globbing**: Use `*` wildcard for partial matches, e.g. `moon doc "String::*rev*"` to find all String methods with "rev" in their name
1388
+
1389
+ ### Examples
1390
+
1391
+ ```bash
1392
+ # search for String methods in standard library:
1393
+ $ moon doc "String"
1394
+
1395
+ type String
1396
+
1397
+ pub fn String::add(String, String) -> String
1398
+ pub fn String::at(String, Int) -> Int
1399
+ # ... more methods omitted ...
1400
+
1401
+ # list all symbols in a standard library package:
1402
+ $ moon doc "@buffer"
1403
+ moonbitlang/core/buffer
1404
+
1405
+ fn from_array(ArrayView[Byte]) -> Buffer
1406
+ fn from_bytes(Bytes) -> Buffer
1407
+ # ... more functions omitted ...
1408
+
1409
+ # list the specific function in a package:
1410
+ $ moon doc "@buffer.new"
1411
+ package "moonbitlang/core/buffer"
1412
+
1413
+ pub fn new(size_hint? : Int) -> Buffer
1414
+ Creates a new extensible buffer with specified initial capacity. If the
1415
+ initial capacity is less than 1, the buffer will be initialized with capacity
1416
+ 1.
1417
+ # ... more details omitted ...
1418
+
1419
+ $ moon doc "String::*rev*"
1420
+ package "moonbitlang/core/string"
1421
+
1422
+ pub fn String::rev(String) -> String
1423
+ Returns a new string with the characters in reverse order. It respects
1424
+ Unicode characters and surrogate pairs but not grapheme clusters.
1425
+
1426
+ pub fn String::rev_find(String, StringView) -> Int?
1427
+ Returns the offset (charcode index) of the last occurrence of the given
1428
+ substring. If the substring is not found, it returns None.
1429
+
1430
+ # ... more details omitted ...
1431
+
1432
+ **Best practice**: When implementing a feature, start with `moon doc` queries to discover available APIs before writing code. This is faster and more accurate than searching through files.
1433
+
1434
+ ```
1435
+
1436
+ ## `moon ide peek-def` for Definition Lookup
1437
+
1438
+ Use this when you want inline context for a symbol without jumping files.
1439
+
1440
+ ```file src/parse.mbt
1441
+ L45:|///|
1442
+ L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
1443
+ L47:| ...
1444
+ ...:| }
1445
+ ```
1446
+
1447
+ ```bash
1448
+ $ moon ide peek-def -symbol Parser -loc src/parse.mbt:46:4
1449
+ Definition found at file src/parse.mbt
1450
+ | ///|
1451
+ 2 | priv struct Parser {
1452
+ | ^^^^^^
1453
+ | bytes : Bytes
1454
+ | mut pos : Int
1455
+ | }
1456
+ |
1457
+ | ///|
1458
+ | fn Parser::new(bytes : Bytes) -> Parser {
1459
+ | { bytes, pos: 0 }
1460
+ | }
1461
+ |
1462
+ | ///|
1463
+ | fn Parser::eof(self : Parser) -> Bool {
1464
+ | self.pos >= self.bytes.length()
1465
+ | }
1466
+ |
1467
+ ```
1468
+
1469
+ For the `-loc` argument, the line number must be precise; the column can be approximate since `-symbol` helps locate the position.