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.
- package/.agents/skills/moonbit-agent-guide/LICENSE +202 -0
- package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +1126 -0
- package/.agents/skills/moonbit-agent-guide/SKILL.md +1126 -0
- package/.agents/skills/moonbit-agent-guide/ide.md +116 -0
- package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +106 -0
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +422 -0
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +422 -0
- package/.agents/skills/moonbit-practice/SKILL.md +258 -0
- package/.agents/skills/moonbit-practice/assets/ci.yaml +25 -0
- package/.agents/skills/moonbit-practice/reference/agents.md +1469 -0
- package/.agents/skills/moonbit-practice/reference/configuration.md +228 -0
- package/.agents/skills/moonbit-practice/reference/ffi.md +229 -0
- package/.agents/skills/moonbit-practice/reference/ide.md +189 -0
- package/.agents/skills/moonbit-practice/reference/performance.md +217 -0
- package/.agents/skills/moonbit-practice/reference/refactor.md +154 -0
- package/.agents/skills/moonbit-practice/reference/stdlib.md +351 -0
- package/.agents/skills/moonbit-practice/reference/testing.md +228 -0
- package/.agents/skills/moonbit-refactoring/LICENSE +21 -0
- package/.agents/skills/moonbit-refactoring/SKILL.md +323 -0
- package/.githooks/README.md +23 -0
- package/.githooks/pre-commit +3 -0
- package/.github/workflows/copilot-setup-steps.yml +40 -0
- package/.turbo/turbo-typecheck.log +2 -0
- package/AGENTS.md +91 -0
- package/LICENSE +21 -0
- package/PLAN.md +64 -0
- package/README.mbt.md +77 -0
- package/README.md +84 -0
- package/TODO.md +120 -0
- package/a.png +0 -0
- package/benchmarks/calc.bench.ts +144 -0
- package/benchmarks/draw.bench.ts +215 -0
- package/benchmarks/load.bench.ts +28 -0
- package/benchmarks/render.bench.ts +53 -0
- package/benchmarks/run.bench.ts +8 -0
- package/benchmarks/types.d.ts +15 -0
- package/docs/scratch-vm-specs/eventloop.md +103 -0
- package/docs/scratch-vm-specs/moonscratch-time-separation.md +50 -0
- package/index.html +91 -0
- package/js/AGENTS.md +5 -0
- package/js/a.ts +52 -0
- package/js/assets/AGENTS.md +5 -0
- package/js/assets/base64.test.ts +14 -0
- package/js/assets/base64.ts +21 -0
- package/js/assets/build-asset.test.ts +26 -0
- package/js/assets/build-asset.ts +28 -0
- package/js/assets/create.test.ts +142 -0
- package/js/assets/create.ts +122 -0
- package/js/assets/index.test.ts +15 -0
- package/js/assets/index.ts +2 -0
- package/js/assets/types.ts +26 -0
- package/js/assets/validation.test.ts +34 -0
- package/js/assets/validation.ts +25 -0
- package/js/assets.test.ts +14 -0
- package/js/assets.ts +1 -0
- package/js/index.test.ts +26 -0
- package/js/index.ts +3 -0
- package/js/render/index.test.ts +65 -0
- package/js/render/index.ts +13 -0
- package/js/render/sharp.ts +87 -0
- package/js/render/svg.ts +68 -0
- package/js/render/types.ts +35 -0
- package/js/render/utils.ts +108 -0
- package/js/render/webgl.ts +274 -0
- package/js/sharp-optional.d.ts +16 -0
- package/js/test/helpers.ts +116 -0
- package/js/test/hikkaku-sample.test.ts +37 -0
- package/js/test/rubik-components.input-motion.test.ts +60 -0
- package/js/test/rubik-components.lists.test.ts +49 -0
- package/js/test/rubik-components.operators.test.ts +104 -0
- package/js/test/rubik-components.pen.test.ts +112 -0
- package/js/test/rubik-components.procedures-loops.test.ts +72 -0
- package/js/test/rubik-components.variables-branches.test.ts +57 -0
- package/js/test/rubik-components.visibility-entry.test.ts +31 -0
- package/js/test/test-projects.ts +598 -0
- package/js/test/variable.ts +200 -0
- package/js/test/warp.test.ts +59 -0
- package/js/vm/AGENTS.md +6 -0
- package/js/vm/README.md +183 -0
- package/js/vm/bindings.test.ts +13 -0
- package/js/vm/bindings.ts +5 -0
- package/js/vm/compare-operators.test.ts +145 -0
- package/js/vm/constants.test.ts +11 -0
- package/js/vm/constants.ts +4 -0
- package/js/vm/effect-guards.test.ts +68 -0
- package/js/vm/effect-guards.ts +44 -0
- package/js/vm/factory.test.ts +486 -0
- package/js/vm/factory.ts +615 -0
- package/js/vm/headless-vm.test.ts +131 -0
- package/js/vm/headless-vm.ts +342 -0
- package/js/vm/index.test.ts +28 -0
- package/js/vm/index.ts +5 -0
- package/js/vm/internal-types.ts +32 -0
- package/js/vm/json.test.ts +40 -0
- package/js/vm/json.ts +273 -0
- package/js/vm/normalize.test.ts +48 -0
- package/js/vm/normalize.ts +65 -0
- package/js/vm/options.test.ts +30 -0
- package/js/vm/options.ts +55 -0
- package/js/vm/pen-transparency.test.ts +115 -0
- package/js/vm/program-wasm.ts +322 -0
- package/js/vm/scheduler-render.test.ts +401 -0
- package/js/vm/scratch-assets.test.ts +136 -0
- package/js/vm/scratch-assets.ts +202 -0
- package/js/vm/types.ts +358 -0
- package/js/vm/value-guards.test.ts +25 -0
- package/js/vm/value-guards.ts +18 -0
- package/moon.mod.json +10 -0
- package/package.json +33 -0
- package/scripts/preinstall.ts +4 -0
- package/src/AGENTS.md +6 -0
- package/src/api.mbt +161 -0
- package/src/api_aot_commands.mbt +184 -0
- package/src/api_effects_json.mbt +72 -0
- package/src/api_options.mbt +60 -0
- package/src/api_program_wasm.mbt +1647 -0
- package/src/api_program_wat.mbt +2206 -0
- package/src/api_snapshot_json.mbt +44 -0
- package/src/cmd/AGENTS.md +5 -0
- package/src/cmd/main/AGENTS.md +5 -0
- package/src/cmd/main/main.mbt +29 -0
- package/src/cmd/main/moon.pkg +7 -0
- package/src/cmd/main/pkg.generated.mbti +13 -0
- package/src/json_helpers.mbt +176 -0
- package/src/moon.pkg +65 -0
- package/src/moonscratch.mbt +3 -0
- package/src/moonscratch_wbtest.mbt +40 -0
- package/src/parser_sb3.mbt +890 -0
- package/src/pkg.generated.mbti +479 -0
- package/src/runtime_eval.mbt +2844 -0
- package/src/runtime_exec.mbt +3850 -0
- package/src/runtime_render.mbt +2550 -0
- package/src/runtime_state.mbt +870 -0
- package/src/test/AGENTS.md +3 -0
- package/src/test/projects/AGENTS.md +6 -0
- package/src/test/projects/moon.pkg +4 -0
- package/src/test/projects/moonscratch_compat_test.mbt +642 -0
- package/src/test/projects/moonscratch_core_test.mbt +1332 -0
- package/src/test/projects/moonscratch_runtime_test.mbt +1087 -0
- package/src/test/projects/pkg.generated.mbti +13 -0
- package/src/test/projects/test_support.mbt +35 -0
- package/src/types_effects.mbt +20 -0
- package/src/types_error.mbt +4 -0
- package/src/types_options.mbt +31 -0
- package/src/types_runtime_structs.mbt +254 -0
- package/src/types_vm.mbt +109 -0
- package/tsconfig.json +29 -0
- package/viewer/index.ts +399 -0
- package/viewer/vite.d.ts +1 -0
- package/viewer/worker.ts +161 -0
- package/vite.config.ts +11 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'MoonBit Performance Tuning'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# MoonBit Performance Tuning
|
|
6
|
+
|
|
7
|
+
## View Types Overview
|
|
8
|
+
|
|
9
|
+
View types are **zero-copy, non-owning, read-only slices**. They don't allocate memory and are ideal for passing sub-sequences without copying data.
|
|
10
|
+
|
|
11
|
+
| Original Type | View Type | Slice Syntax |
|
|
12
|
+
| --------------- | -------------- | ---------------------- |
|
|
13
|
+
| `String` | `StringView` | `s[:]`, `s[start:end]` |
|
|
14
|
+
| `Bytes` | `BytesView` | `b[:]`, `b[start:end]` |
|
|
15
|
+
| `Array[T]` | `ArrayView[T]` | `a[:]`, `a[start:end]` |
|
|
16
|
+
| `FixedArray[T]` | `ArrayView[T]` | `a[:]`, `a[start:end]` |
|
|
17
|
+
|
|
18
|
+
## StringView
|
|
19
|
+
|
|
20
|
+
### Creating StringView
|
|
21
|
+
|
|
22
|
+
```moonbit
|
|
23
|
+
let s = "hello world"
|
|
24
|
+
let view : StringView = s[:] // Entire string
|
|
25
|
+
let hello : StringView = s[0:5] // "hello"
|
|
26
|
+
let world : StringView = s[6:] // "world"
|
|
27
|
+
let partial : StringView = s[:5] // "hello"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### StringView for Function Parameters
|
|
31
|
+
|
|
32
|
+
```moonbit
|
|
33
|
+
///|
|
|
34
|
+
/// BAD: Takes ownership, may copy
|
|
35
|
+
fn process_string(s : String) -> Unit {
|
|
36
|
+
// ...
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
///|
|
|
40
|
+
/// Good: Zero-copy, no allocation
|
|
41
|
+
fn process_string_view(s : StringView) -> Unit {
|
|
42
|
+
// ...
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Both work - implicit conversion
|
|
46
|
+
process_string_view("hello")
|
|
47
|
+
process_string_view(some_string[:])
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Unicode Safety
|
|
51
|
+
|
|
52
|
+
StringView slicing may raise at surrogate boundaries (UTF-16 edge case):
|
|
53
|
+
|
|
54
|
+
```moonbit
|
|
55
|
+
fn safe_slice(s : String, start : Int, end : Int) -> StringView raise {
|
|
56
|
+
s[start:end] // May raise on invalid boundaries
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Or use try! if you're certain
|
|
60
|
+
let view = try! s[start:end]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### StringView Methods
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
moon doc StringView
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Common operations:
|
|
70
|
+
|
|
71
|
+
- `view.length()` - Length in code units
|
|
72
|
+
- `view.to_string()` - Convert back to owned String
|
|
73
|
+
- `view.iter()` - Iterate over characters
|
|
74
|
+
|
|
75
|
+
## ArrayView
|
|
76
|
+
|
|
77
|
+
### Creating ArrayView
|
|
78
|
+
|
|
79
|
+
```moonbit
|
|
80
|
+
let arr = [1, 2, 3, 4, 5]
|
|
81
|
+
let view : ArrayView[Int] = arr[:] // Entire array
|
|
82
|
+
let first3 : ArrayView[Int] = arr[0:3] // [1, 2, 3]
|
|
83
|
+
let last2 : ArrayView[Int] = arr[3:] // [4, 5]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ArrayView for Function Parameters
|
|
87
|
+
|
|
88
|
+
```moonbit
|
|
89
|
+
///|
|
|
90
|
+
/// BAD: Takes ownership
|
|
91
|
+
fn sum_array(arr : Array[Int]) -> Int {
|
|
92
|
+
arr.fold(init=0, fn(a, b) { a + b })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
///|
|
|
96
|
+
/// Good: Zero-copy, accepts slices
|
|
97
|
+
fn sum_view(arr : ArrayView[Int]) -> Int {
|
|
98
|
+
arr.fold(init=0, fn(a, b) { a + b })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Both work
|
|
102
|
+
sum_view([1, 2, 3]) // Array literal
|
|
103
|
+
sum_view(arr[:]) // Full array
|
|
104
|
+
sum_view(arr[1:4]) // Slice
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Pattern Matching with Views
|
|
108
|
+
|
|
109
|
+
```moonbit
|
|
110
|
+
fn process(view : ArrayView[Int]) -> Unit {
|
|
111
|
+
match view {
|
|
112
|
+
[] => println("empty")
|
|
113
|
+
[x] => println("single: \{x}")
|
|
114
|
+
[first, ..rest] => {
|
|
115
|
+
println("first: \{first}")
|
|
116
|
+
process(rest) // rest is ArrayView
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### ArrayView Methods
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
moon doc ArrayView
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Common operations:
|
|
129
|
+
|
|
130
|
+
- `view.length()` - Number of elements
|
|
131
|
+
- `view[i]` - Index access
|
|
132
|
+
- `view.iter()` - Iterator
|
|
133
|
+
- `view.to_array()` - Convert to owned Array
|
|
134
|
+
|
|
135
|
+
## BytesView
|
|
136
|
+
|
|
137
|
+
### Creating BytesView
|
|
138
|
+
|
|
139
|
+
```moonbit
|
|
140
|
+
let bytes : Bytes = b"hello"
|
|
141
|
+
let view : BytesView = bytes[:]
|
|
142
|
+
let first2 : BytesView = bytes[0:2]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### BytesView for Binary Data
|
|
146
|
+
|
|
147
|
+
```moonbit
|
|
148
|
+
///|
|
|
149
|
+
/// Good: Zero-copy binary parsing
|
|
150
|
+
fn parse_header(data : BytesView) -> Header raise ParseError {
|
|
151
|
+
guard data.length() >= 4 else { raise ParseError::TooShort }
|
|
152
|
+
let magic = data[0:2]
|
|
153
|
+
let version = data[2:4]
|
|
154
|
+
// ...
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Performance Patterns
|
|
159
|
+
|
|
160
|
+
### Avoid Unnecessary Allocations
|
|
161
|
+
|
|
162
|
+
```moonbit
|
|
163
|
+
///|
|
|
164
|
+
/// BAD: Creates intermediate strings
|
|
165
|
+
fn join_with_separator(parts : Array[String], sep : String) -> String {
|
|
166
|
+
let mut result = ""
|
|
167
|
+
for i, part in parts {
|
|
168
|
+
if i > 0 { result = result + sep }
|
|
169
|
+
result = result + part // Allocates each time!
|
|
170
|
+
}
|
|
171
|
+
result
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
///|
|
|
175
|
+
/// Good: Use StringBuilder
|
|
176
|
+
fn join_with_separator(parts : Array[String], sep : String) -> String {
|
|
177
|
+
let sb = StringBuilder::new()
|
|
178
|
+
for i, part in parts {
|
|
179
|
+
if i > 0 { sb.write_string(sep) }
|
|
180
|
+
sb.write_string(part)
|
|
181
|
+
}
|
|
182
|
+
sb.to_string()
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Use Views in Recursive Functions
|
|
187
|
+
|
|
188
|
+
```moonbit
|
|
189
|
+
///|
|
|
190
|
+
/// Good: No copying in recursion
|
|
191
|
+
fn binary_search(arr : ArrayView[Int], target : Int) -> Int? {
|
|
192
|
+
guard arr.length() > 0 else { None }
|
|
193
|
+
let mid = arr.length() / 2
|
|
194
|
+
if arr[mid] == target {
|
|
195
|
+
Some(mid)
|
|
196
|
+
} else if arr[mid] > target {
|
|
197
|
+
binary_search(arr[:mid], target)
|
|
198
|
+
} else {
|
|
199
|
+
binary_search(arr[mid + 1:], target).map(fn(i) { i + mid + 1 })
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Converting Back to Owned Types
|
|
205
|
+
|
|
206
|
+
When you need ownership:
|
|
207
|
+
|
|
208
|
+
```moonbit
|
|
209
|
+
let string_view : StringView = "hello"[:]
|
|
210
|
+
let owned_string : String = string_view.to_string()
|
|
211
|
+
|
|
212
|
+
let array_view : ArrayView[Int] = [1, 2, 3][:]
|
|
213
|
+
let owned_array : Array[Int] = array_view.to_array()
|
|
214
|
+
|
|
215
|
+
let bytes_view : BytesView = b"data"[:]
|
|
216
|
+
let owned_bytes : Bytes = bytes_view.to_bytes()
|
|
217
|
+
```
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'MoonBit Refactoring Patterns'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# MoonBit Refactoring Patterns
|
|
6
|
+
|
|
7
|
+
## Prefer match-if Pattern for Two-way Branches with No-op
|
|
8
|
+
|
|
9
|
+
```moonbit
|
|
10
|
+
let opt : Int? = Some(1)
|
|
11
|
+
|
|
12
|
+
///|
|
|
13
|
+
/// BAD
|
|
14
|
+
match opt {
|
|
15
|
+
Some(v) => println("hello")
|
|
16
|
+
None => ()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
///|
|
|
20
|
+
/// Good
|
|
21
|
+
if opt is Some(v) {
|
|
22
|
+
println("hello")
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Prefer guard for Early Returns
|
|
27
|
+
|
|
28
|
+
Use `guard` when you want to exit early if a condition is not met.
|
|
29
|
+
|
|
30
|
+
```moonbit
|
|
31
|
+
///|
|
|
32
|
+
/// BAD: Deep nesting
|
|
33
|
+
fn get_value(array : Array[Int], index : Int) -> Int? {
|
|
34
|
+
if index >= 0 && index < array.length() {
|
|
35
|
+
Some(array[index])
|
|
36
|
+
} else {
|
|
37
|
+
None
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
///|
|
|
42
|
+
/// Good: Early return with guard
|
|
43
|
+
fn get_value(array : Array[Int], index : Int) -> Int? {
|
|
44
|
+
guard index >= 0 && index < array.length() else { None }
|
|
45
|
+
Some(array[index])
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Combine with pattern matching:
|
|
50
|
+
|
|
51
|
+
```moonbit
|
|
52
|
+
///|
|
|
53
|
+
/// BAD: Deep nesting with match
|
|
54
|
+
fn process(resources : Map[String, Resource], path : String) -> String raise Error {
|
|
55
|
+
match resources.get(path) {
|
|
56
|
+
Some(resource) => {
|
|
57
|
+
match resource {
|
|
58
|
+
PlainText(text) => process(text)
|
|
59
|
+
_ => fail("\{path} is not plain text")
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
None => fail("\{path} not found")
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
///|
|
|
67
|
+
/// Good: Flatten with guard is
|
|
68
|
+
fn process(resources : Map[String, Resource], path : String) -> String raise Error {
|
|
69
|
+
guard resources.get(path) is Some(resource) else { fail("\{path} not found") }
|
|
70
|
+
guard resource is PlainText(text) else { fail("\{path} is not plain text") }
|
|
71
|
+
process(text)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Prefer StringView for String Performance
|
|
76
|
+
|
|
77
|
+
```moonbit
|
|
78
|
+
///|
|
|
79
|
+
/// BAD: String concatenation creates new strings each time
|
|
80
|
+
fn process_string(s : String) -> String {
|
|
81
|
+
...
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
///|
|
|
85
|
+
/// Good: StringView avoids copying
|
|
86
|
+
fn process_string_view(s : StringView) -> Unit {
|
|
87
|
+
...
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Prefer for-in over C-style for
|
|
92
|
+
|
|
93
|
+
```moonbit
|
|
94
|
+
///|
|
|
95
|
+
/// BAD: C-style for
|
|
96
|
+
for i = 0; i < items.length(); i = i + 1 {
|
|
97
|
+
println(items[i])
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
///|
|
|
101
|
+
/// Good: for-in
|
|
102
|
+
for item in items {
|
|
103
|
+
println(item)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
///|
|
|
107
|
+
/// Good: When index is needed
|
|
108
|
+
for i, item in items {
|
|
109
|
+
println("\{i}: \{item}")
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Prefer Arrow Functions for Single Expressions
|
|
114
|
+
|
|
115
|
+
```moonbit
|
|
116
|
+
///|
|
|
117
|
+
/// BAD: Verbose
|
|
118
|
+
let f = fn(x) { x + 1 }
|
|
119
|
+
arr.map(fn(x) { x * 2 })
|
|
120
|
+
|
|
121
|
+
///|
|
|
122
|
+
/// Good: Arrow function
|
|
123
|
+
let f = fn { x => x + 1 }
|
|
124
|
+
arr.map(fn { x => x * 2 })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Use else with for Loops to Return Values
|
|
128
|
+
|
|
129
|
+
```moonbit
|
|
130
|
+
///|
|
|
131
|
+
/// BAD: Using variable to hold result
|
|
132
|
+
fn find_first(arr : Array[Int], target : Int) -> Int? {
|
|
133
|
+
let mut result : Int? = None
|
|
134
|
+
for i in arr {
|
|
135
|
+
if i == target {
|
|
136
|
+
result = Some(i)
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
result
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
///|
|
|
144
|
+
/// Good: Return directly with for-else
|
|
145
|
+
fn find_first(arr : Array[Int], target : Int) -> Int? {
|
|
146
|
+
for i in arr {
|
|
147
|
+
if i == target {
|
|
148
|
+
break Some(i)
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
None
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'MoonBit Standard Library and External Packages'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# MoonBit Standard Library
|
|
6
|
+
|
|
7
|
+
The standard library (`moonbitlang/core`) is **automatically available** - no need to add it to dependencies.
|
|
8
|
+
|
|
9
|
+
## Important Rules
|
|
10
|
+
|
|
11
|
+
- ❌ **DO NOT** use `moon add moonbitlang/core/*`
|
|
12
|
+
- ❌ **DO NOT** add to `"deps"` in `moon.mod.json`
|
|
13
|
+
- ❌ **DO NOT** add to `"import"` in `moon.pkg.json`
|
|
14
|
+
- ✅ **DO** use directly: `@strconv.parse_int()`, `@json.parse()`, etc.
|
|
15
|
+
|
|
16
|
+
## Exploring the Standard Library
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# List all available packages
|
|
20
|
+
moon doc ''
|
|
21
|
+
|
|
22
|
+
# Explore specific package
|
|
23
|
+
moon doc "@json"
|
|
24
|
+
moon doc "@buffer"
|
|
25
|
+
moon doc "@encoding/utf8"
|
|
26
|
+
|
|
27
|
+
# Find specific function
|
|
28
|
+
moon doc "@strconv.parse_int"
|
|
29
|
+
|
|
30
|
+
# Search with glob
|
|
31
|
+
moon doc "String::*find*"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Common Packages
|
|
35
|
+
|
|
36
|
+
### @json - JSON Parsing and Serialization
|
|
37
|
+
|
|
38
|
+
```moonbit
|
|
39
|
+
// Parse JSON
|
|
40
|
+
let value : @json.JsonValue = @json.parse("{\"name\": \"Alice\"}")!
|
|
41
|
+
|
|
42
|
+
// Access fields
|
|
43
|
+
match value {
|
|
44
|
+
{ "name": String(name) } => println(name)
|
|
45
|
+
_ => ()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Serialize to JSON (derive ToJson)
|
|
49
|
+
struct User { name: String; age: Int } derive(ToJson)
|
|
50
|
+
|
|
51
|
+
let user = { name: "Bob", age: 30 }
|
|
52
|
+
let json_str = user.to_json().stringify()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### @buffer - Mutable Byte Buffer
|
|
56
|
+
|
|
57
|
+
```moonbit
|
|
58
|
+
let buf = @buffer.new()
|
|
59
|
+
buf.write_string("Hello")
|
|
60
|
+
buf.write_byte(b' ')
|
|
61
|
+
buf.write_string("World")
|
|
62
|
+
let result = buf.to_string() // "Hello World"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### @strconv - String Conversion
|
|
66
|
+
|
|
67
|
+
```moonbit
|
|
68
|
+
// Parse integers
|
|
69
|
+
let n : Int = @strconv.parse_int("42")!
|
|
70
|
+
let hex : Int = @strconv.parse_int("ff", base=16)!
|
|
71
|
+
|
|
72
|
+
// Parse floats
|
|
73
|
+
let f : Double = @strconv.parse_double("3.14")!
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### @encoding/utf8 - UTF-8 Encoding
|
|
77
|
+
|
|
78
|
+
```moonbit
|
|
79
|
+
// Encode string to UTF-8 bytes
|
|
80
|
+
let bytes : Bytes = @encoding/utf8.encode("Hello 世界")
|
|
81
|
+
|
|
82
|
+
// Decode UTF-8 bytes to string
|
|
83
|
+
let s : String = @encoding/utf8.decode(bytes)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### @hashmap - Hash Map (Alternative to Map)
|
|
87
|
+
|
|
88
|
+
```moonbit
|
|
89
|
+
let map : @hashmap.HashMap[String, Int] = @hashmap.new()
|
|
90
|
+
map.set("a", 1)
|
|
91
|
+
map.set("b", 2)
|
|
92
|
+
let value = map.get("a") // Some(1)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### @hashset - Hash Set
|
|
96
|
+
|
|
97
|
+
```moonbit
|
|
98
|
+
let set : @hashset.HashSet[Int] = @hashset.new()
|
|
99
|
+
set.insert(1)
|
|
100
|
+
set.insert(2)
|
|
101
|
+
set.contains(1) // true
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### @sorted_map / @sorted_set - Sorted Collections
|
|
105
|
+
|
|
106
|
+
```moonbit
|
|
107
|
+
// Keys are kept in sorted order
|
|
108
|
+
let map : @sorted_map.T[String, Int] = @sorted_map.new()
|
|
109
|
+
map.insert("b", 2)
|
|
110
|
+
map.insert("a", 1)
|
|
111
|
+
// Iteration: a, b (sorted)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### @random - Random Number Generation
|
|
115
|
+
|
|
116
|
+
```moonbit
|
|
117
|
+
let rng = @random.new()
|
|
118
|
+
let n = rng.int() // Random Int
|
|
119
|
+
let f = rng.double() // Random Double [0, 1)
|
|
120
|
+
let arr = [1, 2, 3, 4, 5]
|
|
121
|
+
rng.shuffle(arr) // Shuffle in place
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### @time - Time and Duration
|
|
125
|
+
|
|
126
|
+
```moonbit
|
|
127
|
+
let now = @time.now()
|
|
128
|
+
let duration = @time.Duration::from_seconds(5)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### @result - Result Utilities
|
|
132
|
+
|
|
133
|
+
```moonbit
|
|
134
|
+
let ok : Result[Int, String] = Ok(42)
|
|
135
|
+
let err : Result[Int, String] = Err("failed")
|
|
136
|
+
|
|
137
|
+
// Map over success
|
|
138
|
+
ok.map(fn(n) { n * 2 }) // Ok(84)
|
|
139
|
+
|
|
140
|
+
// Unwrap with default
|
|
141
|
+
err.unwrap_or(0) // 0
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### @option - Option Utilities
|
|
145
|
+
|
|
146
|
+
```moonbit
|
|
147
|
+
let some : Int? = Some(42)
|
|
148
|
+
let none : Int? = None
|
|
149
|
+
|
|
150
|
+
some.map(fn(n) { n * 2 }) // Some(84)
|
|
151
|
+
some.unwrap_or(0) // 42
|
|
152
|
+
none.unwrap_or(0) // 0
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Collections Overview
|
|
156
|
+
|
|
157
|
+
| Type | Description | Use Case |
|
|
158
|
+
| ------------------------ | ------------------ | ------------------------------ |
|
|
159
|
+
| `Array[T]` | Resizable array | General purpose |
|
|
160
|
+
| `FixedArray[T]` | Fixed-size array | Known size, no resize |
|
|
161
|
+
| `Map[K, V]` | Ordered hash map | Key-value with insertion order |
|
|
162
|
+
| `@hashmap.HashMap[K, V]` | Hash map | Fast lookup |
|
|
163
|
+
| `@sorted_map.T[K, V]` | Sorted map | Ordered by keys |
|
|
164
|
+
| `@list.T[T]` | Linked list | Functional programming |
|
|
165
|
+
| `@deque.T[T]` | Double-ended queue | Queue/stack operations |
|
|
166
|
+
|
|
167
|
+
## Traits in Standard Library
|
|
168
|
+
|
|
169
|
+
### Show - String Representation
|
|
170
|
+
|
|
171
|
+
```moonbit
|
|
172
|
+
struct Point { x: Int; y: Int } derive(Show)
|
|
173
|
+
|
|
174
|
+
let p = { x: 1, y: 2 }
|
|
175
|
+
println(p.to_string()) // "{x: 1, y: 2}"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### ToJson / FromJson - JSON Serialization
|
|
179
|
+
|
|
180
|
+
```moonbit
|
|
181
|
+
struct Config {
|
|
182
|
+
name: String
|
|
183
|
+
value: Int
|
|
184
|
+
} derive(ToJson, FromJson)
|
|
185
|
+
|
|
186
|
+
let config : Config = @json.from_json(@json.parse(json_str)!)!
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Eq / Compare - Equality and Ordering
|
|
190
|
+
|
|
191
|
+
```moonbit
|
|
192
|
+
struct Version { major: Int; minor: Int } derive(Eq, Compare)
|
|
193
|
+
|
|
194
|
+
let v1 = { major: 1, minor: 0 }
|
|
195
|
+
let v2 = { major: 1, minor: 1 }
|
|
196
|
+
v1 < v2 // true
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Hash - Hashing
|
|
200
|
+
|
|
201
|
+
```moonbit
|
|
202
|
+
struct Key { id: Int; name: String } derive(Hash, Eq)
|
|
203
|
+
|
|
204
|
+
// Can be used as HashMap key
|
|
205
|
+
let map : @hashmap.HashMap[Key, String] = @hashmap.new()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Default - Default Values
|
|
209
|
+
|
|
210
|
+
```moonbit
|
|
211
|
+
struct Config {
|
|
212
|
+
timeout: Int
|
|
213
|
+
retries: Int
|
|
214
|
+
} derive(Default)
|
|
215
|
+
|
|
216
|
+
let config = Config::default() // { timeout: 0, retries: 0 }
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Iterators
|
|
220
|
+
|
|
221
|
+
```moonbit
|
|
222
|
+
let arr = [1, 2, 3, 4, 5]
|
|
223
|
+
|
|
224
|
+
// Map
|
|
225
|
+
arr.map(fn(x) { x * 2 }) // [2, 4, 6, 8, 10]
|
|
226
|
+
|
|
227
|
+
// Filter
|
|
228
|
+
arr.filter(fn(x) { x % 2 == 0 }) // [2, 4]
|
|
229
|
+
|
|
230
|
+
// Fold
|
|
231
|
+
arr.fold(init=0, fn(acc, x) { acc + x }) // 15
|
|
232
|
+
|
|
233
|
+
// Find
|
|
234
|
+
arr.find(fn(x) { x > 3 }) // Some(4)
|
|
235
|
+
|
|
236
|
+
// Any / All
|
|
237
|
+
arr.any(fn(x) { x > 3 }) // true
|
|
238
|
+
arr.all(fn(x) { x > 0 }) // true
|
|
239
|
+
|
|
240
|
+
// Chaining
|
|
241
|
+
arr.filter(fn(x) { x % 2 == 0 })
|
|
242
|
+
.map(fn(x) { x * 2 }) // [4, 8]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## String Operations
|
|
246
|
+
|
|
247
|
+
```moonbit
|
|
248
|
+
let s = "hello world"
|
|
249
|
+
|
|
250
|
+
s.length() // 11
|
|
251
|
+
s.contains("world") // true
|
|
252
|
+
s.starts_with("hello") // true
|
|
253
|
+
s.ends_with("world") // true
|
|
254
|
+
s.split(" ") // ["hello", "world"]
|
|
255
|
+
s.replace("world", "MoonBit") // "hello MoonBit"
|
|
256
|
+
s.trim() // Remove whitespace
|
|
257
|
+
s.to_upper() // "HELLO WORLD"
|
|
258
|
+
s.to_lower() // "hello world"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Discovering APIs
|
|
262
|
+
|
|
263
|
+
Always use `moon doc` to discover available APIs:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# What methods does Array have?
|
|
267
|
+
moon doc "Array"
|
|
268
|
+
|
|
269
|
+
# What's in the json package?
|
|
270
|
+
moon doc "@json"
|
|
271
|
+
|
|
272
|
+
# Find all parse functions
|
|
273
|
+
moon doc "*parse*"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
# External Packages
|
|
279
|
+
|
|
280
|
+
Unlike `moonbitlang/core`, these packages require explicit installation.
|
|
281
|
+
|
|
282
|
+
## moonbitlang/x - Extended Utilities
|
|
283
|
+
|
|
284
|
+
Experimental and extended utilities not yet in core.
|
|
285
|
+
|
|
286
|
+
### Installation
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
moon add moonbitlang/x
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Common Packages
|
|
293
|
+
|
|
294
|
+
```moonbit
|
|
295
|
+
// @x/fs - File system operations (native/node backend)
|
|
296
|
+
let content = @x/fs.read_to_string("file.txt")
|
|
297
|
+
|
|
298
|
+
// @x/sys - System operations
|
|
299
|
+
let args = @x/sys.get_args()
|
|
300
|
+
let env = @x/sys.get_env()
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Exploring
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
moon doc "@x/fs"
|
|
307
|
+
moon doc "@x/sys"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## moonbitlang/async - Asynchronous Programming
|
|
311
|
+
|
|
312
|
+
Async/await support for MoonBit.
|
|
313
|
+
|
|
314
|
+
### Installation
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
moon add moonbitlang/async
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Important: Import Required for async main/test
|
|
321
|
+
|
|
322
|
+
To use `async fn main` or `async test`, you **must** import `moonbitlang/async` in your `moon.pkg.json`:
|
|
323
|
+
|
|
324
|
+
```json
|
|
325
|
+
{
|
|
326
|
+
"import": ["moonbitlang/async"]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Without this import, `async fn main` and `async test` will not work.
|
|
331
|
+
|
|
332
|
+
### Basic Usage
|
|
333
|
+
|
|
334
|
+
```moonbit
|
|
335
|
+
// Define async function
|
|
336
|
+
async fn fetch_data(url : String) -> String raise {
|
|
337
|
+
// async operations
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Run async code
|
|
341
|
+
@async.run(async fn() {
|
|
342
|
+
let data = fetch_data("https://example.com")!
|
|
343
|
+
println(data)
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Exploring
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
moon doc "@async"
|
|
351
|
+
```
|