Skip to content

feat(dang): add reflection-driven Go FFI#73

Open
vito wants to merge 1 commit into
mainfrom
go-ffi
Open

feat(dang): add reflection-driven Go FFI#73
vito wants to merge 1 commit into
mainfrom
go-ffi

Conversation

@vito

@vito vito commented Jun 2, 2026

Copy link
Copy Markdown
Owner

What

Adds GoFFI, a reflection-driven bridge that exposes arbitrary Go types
and values to Dang code: it derives a Dang Module from a
reflect.Type, wraps a live Go value as a Dang Value, and invokes Go
methods from Dang. This implements phases 1–2 of go-ffi.md (the
Dang-side MVP plus interface bridging).

It's a sibling to the GraphQL/Dagger import path — same target (Dang's
Module/ModuleValue/Value model), different source — so embedders
can expose in-process Go types to scripting code without routing through
GraphQL.

Public API

  • GoFFI (zero value usable) with a configurable NameMapper.
  • Converter{DangType, ToDang, FromDang} + RegisterConverter, for
    bridging interfaces and host-specific types.
  • RegisterType(env, reflect.Type) (*Module, error) — idempotent and
    recursion-safe (caches the *Module before walking fields).
  • WrapGoValue(any) (Value, error).
  • DefaultNameMapper (PascalCase → camelCase; URLurl,
    HTMLBodyhtmlBody).

WrapGoValue produces a *ModuleValue with lazy field entries and
Callable methods, so field access, method binding, and type checking
come for free from the existing Select.Eval machinery.

Mapping rules

Primitives, pointers (nullable), slices, and structs (recursively
registered) map by default. Interfaces require a registered Converter.
Trailing error returns raise a BasicError. Void mutators and
multi-return methods are skipped so registering arbitrary types stays
robust.

Note

Go reflection cannot recover parameter names, so method arguments are
named positionally (arg1, arg2, …). No-arg methods — the common
Booklit case — are unaffected and remain auto-callable.

Tests

10 tests cover the name mapper, type-derivation schemes, field access
and auto-called/argument'd method calls through the full
parse→infer→eval pipeline, (T, error) raising and catching, recursive
types, the unregistered-interface error, an interface Converter, and
the wrap-before-register error.

Phases 3–4 (Booklit integration) are separate, consumer-side follow-ups.

Introduce GoFFI, a bridge that derives a Dang Module from a Go
reflect.Type, wraps live Go values as Dang Values, and invokes Go
methods from Dang code. This is a sibling to the GraphQL/Dagger import
path, targeting the same Module/ModuleValue/Value model from a different
source, so that embedders can expose in-process Go types to scripting
code without routing through GraphQL.

RegisterType installs a type as a class and is recursion-safe by caching
the Module before walking fields. WrapGoValue produces a ModuleValue
with lazy field access and Callable method entries, so field selection,
method binding, and type checking come for free. Primitives, pointers,
slices, and structs map by default; interfaces require a registered
Converter; trailing error returns raise a BasicError; void mutators and
multi-return methods are skipped to stay robust against arbitrary types.

Go reflection cannot recover parameter names, so method arguments are
named positionally (arg1, arg2, ...); no-arg methods, the common case,
are unaffected and remain auto-callable.

Signed-off-by: Alex Suraci <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant