# Moonbit Practice

> MoonBit code generation best practices. Use when writing MoonBit code to avoid common AI mistakes with syntax, tests, and benchmarks.

- Skill: `h3y6e/moonbit-practice` (Agent Skill, multi-file: 15 files)
- Install (CLI): `npx skillmds@latest add h3y6e/moonbit-practice`
- Raw SKILL.md: https://api.skillmd.com/api/skills/h3y6e/moonbit-practice/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: h3y6e (https://skillmd.com/u/h3y6e)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/h3y6e/moonbit-practice

---

# MoonBit Practice Guide

Best practices for AI when generating MoonBit code.
If you don't understand something here, use `moonbit-docs` skill to search the official documentation.

## Guidelines

### Code Navigation: Prefer moon ide over Read/Grep

In MoonBit projects, **prefer `moon ide` commands over Read tool or grep**.

```bash
# ❌ Avoid: Reading files directly
Read src/parser.mbt

# ✅ Recommended: Find definitions from symbols
moon ide peek-def Parser::parse
moon ide goto-definition -tags 'pub fn' -query 'parse'

# ❌ Avoid: Searching with grep
grep -r "fn parse" .

# ✅ Recommended: Semantic search
moon ide find-references parse
moon ide outline src/parser.mbt
```

**Why:**
- `moon ide` provides semantic search (distinguishes definitions from call sites)
- grep picks up comments and strings
- `moon doc` quickly reveals APIs

### Other Rules

- Use `moon doc '<Type>'` to explore APIs before implementing
- Check reference/configuration.md before editing moon.pkg.json / moon.mod.json
- Check reference/language.md for detailed language feature examples (types, traits, pattern matching, etc.)
- Check reference/mbtx.md for single-file `.mbtx` scripts (`moon run script.mbtx`, stdin via `moon run -`, inline `import { }` block; nightly only)

## Common Pitfalls

- **Don't use uppercase for variables/functions** - compilation error
- **`mut` is only for reassignment, not field mutation** - Array push doesn't need it
- **`return` is unnecessary** - last expression is the return value
- **Methods require `Type::` prefix**
- **`++` `--` not supported** - use `i = i + 1` or `i += 1`
- **No `try` needed for error propagation** - automatic (unlike Swift)
- **No `await` keyword** - just declare with `async fn`
- **Prefer range for over C-style** - `for i in 0..<n {...}`
- **`nobreak` not `else`** for functional for-loop exit values (`else` is deprecated)
- **Legacy syntax**: `function_name!(...)` and `function_name(...)?` are deprecated
- **`for { ... }` is deprecated** - use `for ;; { ... }` or `while true { ... }` for infinite loops
- **Cross-package `.` syntax for `impl` removed** - `.` method call only works within the same package

## Common Syntax Mistakes by AI

### Type Parameter Position

```moonbit
///| NG: fn identity[T] is old syntax
fn identity[T](val: T) -> T { val }

///| OK: Type parameter comes right after fn
fn[T] identity(val: T) -> T { val }
```

### raise Syntax

```moonbit
///|
/// NG: -> T!Error was removed
fn parse(s: String) -> Int!Error { ... }

///|
/// OK: Use raise keyword
fn parse(s: String) -> Int raise Error { ... }
```

`Int raise` is shorthand for `Int raise Error`.
async fn implicitly raises by default; use `noraise` to enforce no errors.

### Macro Calls

```moonbit
///|
/// NG: ! suffix was removed
assert_true!(true)

///|
/// OK
assert_true(true)
```

### Multi-line Text

```moonbit
let text =
  #|line 1
  #|line 2
```

### Comments and Block Separators

`///|` is a block separator. `///` comments attach to the following `///|` block.

```moonbit
///|
/// This function is foo
fn foo() -> Unit { ... }

///|
/// This function is bar
fn bar() -> Unit { ... }
```

Avoid consecutive `///|` at the file beginning as they create separate blocks.

## Snapshot Tests

`moon test -u` auto-updates `content=""` in `inspect(val)`.

```moonbit
test "snapshot" {
  inspect([1, 2, 3], content="")  // auto-filled by moon test -u
}
```

After running:

```moonbit
test "snapshot" {
  inspect([1, 2, 3], content="[1, 2, 3]")
}
```

## Doc Tests

Available in `.mbt.md` files or `///|` inline comments.

| Code Block | Behavior |
|------------|----------|
| ` ```mbt check ` | Checked by LSP |
| ` ```mbt test ` | Executed as `test {...}` |
| ` ```moonbit ` | Display only (not executed) |

Example (inline comment):

```moonbit

///|
/// Increment an integer by 1
/// ```mbt test
/// inspect(incr(41), content="42")
/// ```
pub fn incr(x : Int) -> Int {
  x + 1
}
```

## Pre-release Checklist

Run before releasing:

```bash
moon fmt   # Format code
moon info  # Generate type definition files
```

`pkg.generated.mbti` is auto-generated by `moon info`. Don't edit it directly.

## Exploring Built-in Type Methods

```bash
moon doc StringView   # StringView methods
moon doc Array        # Array methods
moon doc Map          # Map methods
```

## Quick Reference

| Topic | Command | Details |
|-------|---------|---------|
| Test | `moon test` | https://docs.moonbitlang.com/en/stable/language/tests |
| Update snapshots | `moon test -u` | Same as above |
| Filtered test | `moon test --filter 'glob'` | Run specific tests |
| Benchmark | `moon bench` | https://docs.moonbitlang.com/en/stable/language/benchmarks |
| Doc Test | `moon check` / `moon test` | https://docs.moonbitlang.com/en/stable/language/docs |
| Format | `moon fmt` | - |
| Generate types | `moon info` | - |
| Doc reference | `moon doc <Type>` | - |
| Workspace init | `moon work init` | See reference/configuration.md |
| Workspace add | `moon work use mod1 mod2` | Add modules to workspace |
| API usage analysis | `moon ide analyze .` | Show public API usage stats |

## moon ide Tools

More accurate than grep for code navigation. See `reference/ide.md` for details.

```bash
# Show symbol definition
moon ide peek-def Parser::read_u32_leb128

# Package outline
moon ide outline .

# Find references
moon ide find-references TranslationUnit

# Jump to type definition (with location)
moon ide peek-def Parser -loc src/parse.mbt:46:4

# Show type signature and docs at location (unlike peek-def, shows inferred type + doc comments)
moon ide hover my_func --loc src/lib.mbt:10:4

# Rename symbol across the project
moon ide rename old_name new_name

# Analyze public API usage (v0.8.3+)
moon ide analyze .              # Current package
moon ide analyze internal/*     # Glob pattern
```

## Functional for loop

Prefer functional for loops whenever possible. More readable and easier to reason about.

```moonbit
// Functional for loop with state
for i = 0, sum = 0; i <= 10 {
  continue i + 1, sum + i  // Update state
} nobreak {
  sum  // Value at loop exit (nobreak, not else)
}

// Range for (recommended)
for i in 0..<n { ... }
for i, v in array { ... }  // index and value

// Range for with extra loop variable (v0.8.3+)
for x in xs; sum = 0 {
  continue sum + x
} nobreak {
  sum
}

// Infinite loop (for { } is deprecated)
for ;; { ... }
```

## String Constants

`const` supports string concatenation and interpolation (v0.8.3+):

```moonbit
const Hello : String = "Hello"
const HelloWorld : String = Hello + " world"
const Message : String =
  $|========
  $|\{HelloWorld}
  $|========
```

## Error Handling

MoonBit uses checked errors. See `reference/ffi.md` for details.

```moonbit
///| Declare error type
suberror ParseError {
  InvalidEof
  InvalidChar(Char)
}

///| Declare with raise, auto-propagates
fn parse(s: String) -> Int raise ParseError {
  if s.is_empty() { raise ParseError::InvalidEof }
  ...
}

///| Convert to Result
let result : Result[Int, ParseError] = try? parse(s)

///| Handle with try-catch
parse(s) catch {
  ParseError::InvalidEof => -1
  _ => 0
}
```

## CI and Publishing

### Third-party actions

Prefer the **official curl installer** (`assets/ci.yaml`) or **Nix with moonbit-overlay** (`assets/ci-nix.yaml`) over third-party actions such as `hustcer/setup-moonbit@v1`. The installer is short and adds nothing to the action supply chain; the Nix path is fully reproducible when a `flake.nix` is present.

### `moon update` is mandatory

Runners start with an empty registry index. Any registry-hosted MoonBit dependency makes `moon check` / `moon test` / `moon build` fail with `Failed to resolve registry dependency` until you run `moon update`. Put it immediately after installing the CLI in every workflow that touches mooncakes.

### Publishing an npm package whose build depends on MoonBit

Some projects (e.g. Vite plugins, language tooling) ship as npm packages but run `moon` inside `pnpm build` — for instance `pnpm build:parser` that calls `moon -C tools/parser build --release --target js`. In that case the npm publish workflow needs both the MoonBit CLI and `moon update` **before** `pnpm build`, otherwise the build fails on CI.

See `assets/publish-to-npm.yaml` for a minimal release-triggered publish workflow that uses OIDC Trusted Publishing (no `NPM_TOKEN`) and sets up MoonBit correctly. Pair it with a release automation tool (see the `npm-release` skill for a release-please + OIDC setup) to avoid manual `npm publish` calls.

## Assets

- `assets/ci.yaml` — GitHub Actions CI (curl installer)
- `assets/ci-nix.yaml` — GitHub Actions CI with Nix (moonbit-overlay)
- `assets/publish-to-npm.yaml` — release-triggered npm publish with MoonBit build and OIDC Trusted Publishing

### Nix Setup (moonbit-overlay)

[moonbit-community/moonbit-overlay](https://github.com/moonbit-community/moonbit-overlay) provides a Nix flake overlay for reproducible MoonBit builds.

Minimal `flake.nix` for a MoonBit project:

```nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    moonbit-overlay.url = "github:moonbit-community/moonbit-overlay";
    moon-registry = {
      url = "git+https://mooncakes.io/git/index";
      flake = false;
    };
  };

  outputs = { nixpkgs, moonbit-overlay, moon-registry, ... }:
    let
      system = "x86_64-linux"; # or aarch64-darwin, etc.
      pkgs = import nixpkgs {
        inherit system;
        overlays = [ moonbit-overlay.overlays.default ];
      };
      moonHome = pkgs.moonPlatform.bundleWithRegistry {
        cachedRegistry = pkgs.moonPlatform.buildCachedRegistry {
          moonModJson = ./moon.mod.json;
          registryIndexSrc = moon-registry;
        };
      };
    in {
      devShells.${system}.default = pkgs.mkShellNoCC {
        packages = [ moonHome pkgs.git ];
        env.MOON_HOME = "${moonHome}";
      };
    };
}
```

Key APIs from the overlay:
- `pkgs.moonPlatform.buildMoonPackage` - Build a MoonBit package as a Nix derivation
- `pkgs.moonPlatform.bundleWithRegistry` - Create a MOON_HOME with cached registry
- `pkgs.moonPlatform.buildCachedRegistry` - Pre-fetch mooncakes registry dependencies

