| name | builtin-dsl |
| description | How to add or modify builtin functions and methods in `pkg/dang/stdlib.go`. Use when extending Dang's standard library. |
Builtin Function/Method DSL
All builtins are defined in a single place: the registerStdlib() function in
pkg/dang/stdlib.go. The DSL handles type registration, eval-env registration,
method dispatch, and default-value application — you only edit one file.
Design principle: prefer methods over global functions
Avoid polluting the global namespace. Methods on types compose better with
autocomplete and chaining.
"hello".toUpper() not toUpper("hello")
users.length not len(users)
"a,b".split(",") not split("a,b", ",")
Only add a global function when it doesn't naturally belong to one type
(print, assert).
Adding a global function
Builtin("print").
Doc("prints a value to stdout").
Example(`print("hello, world")`).
Params("value", TypeVar('a')).
Returns(TypeVar('n')).
Impl(func(ctx context.Context, args Args) (Value, error) {
val, _ := args.Get("value")
writer := ioctx.StdoutFromContext(ctx)
fmt.Fprintln(writer, val.String())
return NullValue{}, nil
})
Adding a method
Method(StringType, "toUpper").
Doc("converts a string to uppercase").
Example(`"hello".toUpper`).
Returns(NonNull(StringType)).
Impl(func(ctx context.Context, self Value, args Args) (Value, error) {
str := self.(StringValue).Val
return ToValue(strings.ToUpper(str))
})
Receiver types currently available: StringType, IntType, BooleanType.
Custom scalar receivers dispatch generically: Select.Eval has a
ScalarValue case that resolves builtin methods via
GetMethodKey(v.ScalarType, name) and then the type's Dang-defined methods,
so registering Method(SomeScalarType, ...) on a ScalarValue-backed scalar
needs no new dispatch code.
Consider self-hosting instead
Before writing a builtin in Go, consider whether it belongs in the
Dang-source prelude (pkg/dang/prelude/*.dang): stdlib expressible in
the core language (string/list manipulation, string-refinement scalars with
methods and a new() hook) lives there — Path is the model. Prelude
members document themselves with a prose docstring plus an @example
directive whose argument is a ```dang fenced template holding a
runnable snippet (enforced + evaluated by TestPreludeExamples), and the
docs reference renders them via the same card machinery. Go builtins remain
for anything needing Go (I/O, regexp engine, crypto/random).
Examples are required
Every builtin must declare .Example(...): a tiny, self-contained snippet of
Dang that evaluates to something illustrative. The stdlib reference renders it
as a pre-seeded, runnable REPL, and two tests enforce it
(pkg/dang/stdlib_examples_test.go): one fails if any builtin lacks an example,
the other parses + type-checks + evaluates every example so it can't drift from
the implementation. Keep examples runnable in the core language only (no GraphQL
imports), and write them as a reader would call the builtin — e.g.
`"a,b,c".split(",")`, `[1, 2, 3].map { x => x * 2 }`,
`Random.int(1, 7)`. A regex example needs a Go double-quoted string since
Dang regex literals use backticks: Example("\"abc123\".containsMatch(`\\d+`)").
Optional parameters with defaults
Include the default value after the type in Params():
Params(
"separator", NonNull(StringType),
"limit", IntType, IntValue{Val: 0},
)
Type helpers
TypeVar(rune) — type variable, e.g. TypeVar('a')
NonNull(t) — non-null wrapper
ListOf(t) — list type
Args accessors
args.Get(name) (Value, bool), args.GetString, args.GetInt, args.GetBool,
args.GetList, args.Require (panics if missing).
ToValue — the single source of truth for Go→Dang
Use ToValue(any) (Value, error) to convert Go values. It's shared between
stdlib.go, eval.go (GraphQL result conversion), and ast_expressions.go
(GraphQL field values). If you need a new Go→Dang conversion, extend
ToValue rather than open-coding it.
Supported: nil, any Value, string, all int kinds (float truncates),
bool, []string, []int, []bool, []any (element type inferred).
Workflow
- Edit
pkg/dang/stdlib.go inside registerStdlib().
- Run tests:
go test ./tests/ -run "TestDang/TestLanguage/your_test" -v.