Guides development of AILANG builtin functions. Use when user wants to add a builtin function, register new builtins, or understand the builtin system. Reduces development time from 7.5h to 2.5h (-67%).
Scanned 2/12/2026
Install to Claude Code
npx -y skills add sunholo-data/ailang --skill builtin-developer --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Builtin Developer?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/sunholo-data-builtin-developer)More formats (shields.io, HTML) on the badges page.
---
name: Builtin Developer
description: Guides development of AILANG builtin functions. Use when user wants to add a builtin function, register new builtins, or understand the builtin system. Reduces development time from 7.5h to 2.5h (-67%).
---
# Builtin Developer
Add new builtin functions to AILANG using the modern registry system (M-DX1).
## Quick Start
**Development time: ~2.5 hours** (down from 7.5h with legacy system)
**Most common workflow:**
1. Register builtin with metadata (~30 min)
2. Write hermetic tests (~1 hour)
3. Validate and inspect (~30 min)
4. Runtime auto-wires (already done!)
## When to Use This Skill
Invoke this skill when:
- User wants to add a new builtin function
- User asks about builtin registration process
- User needs to understand the builtin system
- User wants to validate or inspect builtins
- User asks "how do I add a builtin?"
## System Overview
**Status**: 🎉 **M-DX1 COMPLETE (Oct 2025)** - 52 builtins migrated, fully documented
**Key Benefits:**
- **67% faster**: 2.5h instead of 7.5h
- **1 file instead of 4**: Single-point registration
- **71% less code**: Type Builder DSL reduces boilerplate
- **Automatic wiring**: Registry connects to runtime/link
- **Hermetic tests**: MockEffContext with HTTP/FS mocking
**Components:**
- **Central Registry** (`internal/builtins/spec.go`) - Single registration point
- **Type Builder DSL** (`internal/types/builder.go`) - Fluent type construction
- **Test Harness** (`internal/effects/testctx/`) - Hermetic testing helpers
- **CLI Commands** - Validation and inspection tools
## Available Scripts
### `scripts/validate_builtins.sh`
Validate all builtins in the registry.
**Usage:**
```bash
.claude/skills/builtin-developer/scripts/validate_builtins.sh
```
**What it checks:**
- All builtins are registered
- Proper metadata structure
- Type signatures valid
- Test coverage exists
### `scripts/check_builtin_health.sh`
Run ailang doctor and list commands.
**Usage:**
```bash
.claude/skills/builtin-developer/scripts/check_builtin_health.sh
```
## Workflow
### Step 1: Register the Builtin (~30 min)
**Create or edit module file** (e.g., `internal/builtins/string.go`):
```go
// internal/builtins/string.go
func init() {
registerMyBuiltin()
}
func registerMyBuiltin() {
RegisterEffectBuiltin(BuiltinSpec{
Module: "std/string",
Name: "_str_reverse",
NumArgs: 1,
IsPure: true, // or false with Effect: "IO"
Type: makeReverseType,
Impl: strReverseImpl,
Metadata: &BuiltinMetadata{
Description: "Reverse a string (Unicode-aware)",
Params: []ParamDoc{
{Name: "s", Description: "String to reverse"},
},
Returns: "Reversed string",
Examples: []Example{
{Code: `_str_reverse("hello")`, Description: "Returns \"olleh\""},
},
Since: "v0.3.15",
Stability: StabilityStable,
Tags: []string{"string", "reverse", "unicode"},
Category: "string",
},
})
}
func makeReverseType() types.Type {
T := types.NewBuilder()
return T.Func(T.String()).Returns(T.String())
}
func strReverseImpl(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) {
str := args[0].(*eval.StringValue).Value
runes := []rune(str)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return &eval.StringValue{Value: string(runes)}, nil
}
```
**Key points:**
- Use `RegisterEffectBuiltin()` in `init()`
- Use Type Builder DSL for type construction
- Include complete metadata (description, params, examples, tags)
- Set `IsPure: true` for pure functions, or `Effect: "IO"` for effects
**See:** [resources/type_builder_examples.md](resources/type_builder_examples.md) for type patterns
### Step 2: Write Hermetic Tests (~1 hour)
**Add tests in** `internal/builtins/register_test.go`:
```go
func TestStrReverse(t *testing.T) {
ctx := testctx.NewMockEffContext()
tests := []struct {
input string
expected string
}{
{"hello", "olleh"},
{"", ""},
{"🎉", "🎉"},
}
for _, tt := range tests {
result, err := strReverseImpl(ctx, []eval.Value{
testctx.MakeString(tt.input),
})
assert.NoError(t, err)
assert.Equal(t, tt.expected, testctx.GetString(result))
}
}
```
**Test Harness helpers:**
- `testctx.NewMockEffContext()` - Create test context
- `testctx.MakeString()`, `MakeInt()`, `MakeRecord()` - Create test values
- `testctx.GetString()`, `GetInt()`, `GetRecord()` - Extract values
**For HTTP/FS effects:**
```go
ctx := testctx.NewMockEffContext()
ctx.GrantAll("Net") // Grant Net capability
ctx.SetHTTPClient(server.Client()) // Use test server
```
**See:** [resources/testing_patterns.md](resources/testing_patterns.md) for more examples
### Step 3: Validate and Inspect (~30 min)
**Run validation:**
```bash
# Validate all builtins
ailang doctor builtins
# List all builtins by module
ailang builtins list --by-module
# Check for orphaned builtins (migration safety)
ailang builtins check-migration
```
**Run tests:**
```bash
# Test specific builtin
go test -v internal/builtins -run TestStrReverse
# Test all builtins
make test
```
**Test in REPL:**
```bash
ailang repl
> :type _str_reverse
string -> string
> _str_reverse("hello")
"olleh" : string
```
### Step 4: Runtime Wiring (Automatic!)
No manual wiring needed! The registry automatically:
- ✅ Registers with evaluator (`internal/eval`)
- ✅ Adds to type environment (`internal/types`)
- ✅ Links to runtime (`internal/runtime`)
- ✅ Generates module interface (`internal/link`)
**Feature flag removed in v0.3.10** - New registry is now the default.
## Key Components
### Central Registry
**Location:** `internal/builtins/spec.go`
**Features:**
- Single registration point with `RegisterEffectBuiltin()`
- Compile-time validation (arity, types, impl, effects)
- Freeze-safe (no registration after init)
- 52 builtins migrated
### Type Builder DSL
**Location:** `internal/types/builder.go`
**Reduces type construction from 35 lines → 10 lines (-71%)**
**Available methods:**
```go
T := types.NewBuilder()
// Primitive types
T.String()
T.Int()
T.Float()
T.Bool()
T.Unit()
// Complex types
T.List(elementType)
T.Record(fields...)
T.Tuple(types...)
T.Con("TypeName")
T.Var("α")
// Function types
T.Func(arg1, arg2, ...).Returns(returnType).Effects("IO", "FS")
// Type applications
T.App("Result", okType, errType)
```
**See:** [resources/type_builder_examples.md](resources/type_builder_examples.md)
### Test Harness
**Location:** `internal/effects/testctx/`
**Value constructors:**
- `MakeString(s string)` → StringValue
- `MakeInt(i int)` → IntValue
- `MakeFloat(f float64)` → FloatValue
- `MakeBool(b bool)` → BoolValue
- `MakeList([]Value)` → ListValue
- `MakeRecord(map[string]Value)` → RecordValue
**Value extractors:**
- `GetString(Value)` → string
- `GetInt(Value)` → int
- `GetFloat(Value)` → float64
- `GetBool(Value)` → bool
- `GetList(Value)` → []Value
- `GetRecord(Value)` → map[string]Value
**Effect mocking:**
- `ctx.GrantAll(effect)` - Grant capabilities
- `ctx.SetHTTPClient(client)` - Mock HTTP
- `ctx.SetFS(fs)` - Mock filesystem
### Validation & Inspection
**Commands:**
```bash
ailang doctor builtins # Health checks
ailang builtins list # List all builtins
ailang builtins list --by-module # Group by module
ailang builtins list --by-effect # Group by effect
ailang builtins check-migration # Check for orphaned builtins
```
**6 validation rules:**
1. Type function exists and valid
2. Implementation function exists
3. Arity matches NumArgs
4. Effect consistency (IsPure vs Effect field)
5. Module name valid
6. Metadata complete
## Metrics
**Development time:**
| Task | Before | After | Savings |
|------|--------|-------|---------|
| Files to edit | 4 | 1 | -75% |
| Type construction | 35 LOC | 10 LOC | -71% |
| Total time | 7.5h | 2.5h | -67% |
| Test setup | ~50 LOC | ~15 LOC | -70% |
## Resources
### Detailed Examples
**For type patterns:**
- [resources/type_builder_examples.md](resources/type_builder_examples.md) - All type patterns
**For testing patterns:**
- [resources/testing_patterns.md](resources/testing_patterns.md) - Hermetic test examples
**For comprehensive guide:**
- [M-DX1-FINAL-SUMMARY.md](../../../M-DX1-FINAL-SUMMARY.md) - Complete M-DX1 summary
- `design_docs/planned/easier-ailang-dev.md` - Design rationale
- `CHANGELOG.md` (v0.3.10+) - Version history
### File Locations
**Builtin files by module:**
- `internal/builtins/string.go` - String functions (9 builtins)
- `internal/builtins/math.go` - Math functions (37 builtins)
- `internal/builtins/io.go` - IO functions (3 builtins)
- `internal/builtins/net.go` - Network functions (1 builtin)
- `internal/builtins/show.go` - Show typeclass (1 builtin)
- `internal/builtins/json_decode.go` - JSON decoding (1 builtin)
**Test files:**
- `internal/builtins/*_test.go` - Builtin tests
- `internal/effects/testctx/*_test.go` - Test harness tests
## Common Patterns
### Pattern 0: "Zero-Arg" Builtins (M-DX10 Unit-Argument Model)
**⚠️ AILANG has no true nullary functions!** All functions that appear to take no arguments must take a `unit` parameter.
```go
// ✅ CORRECT - Unit-argument model
RegisterEffectBuiltin(BuiltinSpec{
Module: "std/sharedmem",
Name: "_sharedmem_keys",
NumArgs: 1, // Takes unit parameter!
Effect: "SharedMem",
Type: func() types.Type {
T := types.NewBuilder()
return T.Func(T.Unit()).Returns(T.List(T.String())).Effects("SharedMem")
},
Impl: func(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) {
// args[0] is unit, ignored (validates arity)
keys := ctx.SharedMem.Cache.Keys()
// ...
},
})
// ❌ WRONG - True nullary (causes "arity mismatch: 0 vs 1")
RegisterEffectBuiltin(BuiltinSpec{
Name: "_sharedmem_keys",
NumArgs: 0, // BUG!
})
```
**Stdlib wrapper:**
```ailang
export func keys(u: unit) -> list[string] ! {SharedMem} {
_sharedmem_keys(u)
}
-- Or expression body:
export func now() -> int ! {Clock} = _clock_now()
```
**Go tests must pass unit:**
```go
// ✅ Correct
result, err := impl(ctx, []eval.Value{&eval.UnitValue{}})
// ❌ Wrong - causes arity mismatch
result, err := impl(ctx, []eval.Value{})
```
**Reference:** [M-DX10 design doc](../../../design_docs/implemented/v0_4_6/m-dx10-nullary-function-calls.md)
### Pattern 1: Pure String Function
```go
RegisterEffectBuiltin(BuiltinSpec{
Module: "std/string",
Name: "_str_len",
NumArgs: 1,
IsPure: true,
Type: func() types.Type {
T := types.NewBuilder()
return T.Func(T.String()).Returns(T.Int())
},
Impl: func(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) {
s := args[0].(*eval.StringValue).Value
return &eval.IntValue{Value: len([]rune(s))}, nil
},
})
```
### Pattern 2: Effect Function with HTTP
```go
RegisterEffectBuiltin(BuiltinSpec{
Module: "std/net",
Name: "_net_httpRequest",
NumArgs: 4,
Effect: "Net",
Type: makeHTTPRequestType,
Impl: effects.NetHTTPRequest, // Uses ctx.GetHTTPClient()
})
```
### Pattern 3: Complex Record Types
**See:** [resources/type_builder_examples.md](resources/type_builder_examples.md) for full example with nested records.
## Progressive Disclosure
This skill loads information progressively:
1. **Always loaded**: This SKILL.md file (workflow overview)
2. **Execute as needed**: Scripts in `scripts/` (validation)
3. **Load on demand**: Detailed type examples in `resources/`
## Notes
- No feature flag needed (default since v0.3.10)
- Registry auto-wires to all subsystems
- Test harness prevents network/FS side effects
- Metadata enables future tooling (docs, LSP)
- Type Builder DSL is type-safe at compile time
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!