Transpile to idiomatic Go
Every.ft file compiles to Go source in your module. The output is an ordinary, readable Go source file. Struct fields, error returns, and control flow follow familiar Go patterns.
Shape fields lower to unexported Go identifiers by default. For JSON marshaling (sidecar invoke, HTTP handlers), enable -export-struct-fields or compiler.exportStructFields in ftconfig.json to emit exported fields with json tags.
Compile a .ft file to Go:
Aligned with Go
Much of Forst mirrors Go syntax:package, import, func, if/for/range, defer, go goroutines. A minimal program can be identical in Forst and Go. See Language overview.
Forst adds structural typing, constraints, ensure, and nominal errors on top of that base.
Import Go packages
Call into the standard library and third-party modules with qualified names:- Forst
- Generated Go
go/packages (or the last path segment as a fallback before load). That local name must be a valid Forst identifier and cannot be a Forst or Go keyword. Add an explicit alias when the default would be invalid, for example import typePkg "fmt" instead of relying on a path segment named type. The alias node is allowed on Go imports (import node "fmt") even though node is reserved for TypeScript opt-in imports.
The compiler loads Go packages via go/packages and type-checks Forst↔Go calls when imports resolve. Supported mappings include primitives, slices, pointers, error, interface{} (including variadic calls), and Forst func literals at Go func parameters.
Calls with several return values become a Tuple when captured in one
variable. Split the values and use ensure !err or err when your function
should expose an exclusive Result.
Keeping
Result and Tuple separate prevents useful values from being
ignored and keeps success checks reliable. Read why the types stay
separate for the
common pitfalls and the rules Forst uses to avoid them.Slice subslices
Forst slice values ([]T) support three-index subslice syntax, lowered to Go slice expressions:
xs[low:high]— bounded rangexs[low:]— from index through endxs[:high]— from start through index
examples/in/slices.ft. Subslices are a language feature on Forst slice values. They are distinct from variadic spread at Go call sites (next section).
Variadic spread into Go calls
When a Go function takes a variadic parameter, spread a Forst slice subslice at the call site withexpr[low:].... The compiler lowers this to a Go variadic argument list.
- Forst
- Generated Go
examples/in/go_interop/cli.ft and helpers.go.
Fields and methods on Go values
After a Go call or a local binding typed from Go, use field access and methods with dotted paths:cmd.Run()— method on a*exec.Cmdbindingcmd.ProcessState.ExitCode()— field then method
exec.Command(...) are checked when the stdlib package loads from your module workspace (go.mod walk-up sets GoWorkspaceDir).
Go function callbacks
Pass a Forst function literal where Go expects a concretefunc type—for example http.HandleFunc. See examples/in/go_interop/http_handle.ft.
Forst packages in the same module
Animport "module/path" can refer to another Forst package in the same Go module—not only hand-written Go. The compiler resolves these as Forst siblings from .ft sources and the module import map (modulecheck). Cross-package calls type-check against the sibling package’s Forst signatures.
This matters for Providers cross-package wiring: api can call auth.LogEvent without a committed Go stub. forst test emits ephemeral lib shims under .forst/gen/test/; the LSP uses the same module-level pass so editor diagnostics match compile-time arity.
See the cross-package provider example examples/in/rfc/providers/cross_pkg/ (auth + api).
Do not confuse sibling Forst imports with Go package imports: import "fmt" loads Go via go/packages; import "yourmod/auth" loads a Forst package when auth/ contains .ft files declaring package auth.
Mixed packages
Place.ft files alongside .go in the same module:
-root to merge same-package .ft files that share one directory under a tree, matching discovery for forst run, forst build, and the dev server:
package name in sibling directories is rejected. Put every Forst package in its own directory when you split files.
The typechecker walks up from your .ft file to find go.mod (GoWorkspaceDir). Exported funcs from co-located .go files resolve without an import line (same package). Example: examples/in/go_interop/helpers.go called from custom.ft / cli.ft.
Project layout profiles
Forst supports three common Go module layouts. Pick one and stay consistent within a project.forst run transpiles your entry, writes a temp sandbox under .forst/run/ with its own go.mod, and executes via go run. Go-native projects (root go.mod, not only .forst-gomod/) use Mode B: an auto-managed .forst/go.work workspace instead of a fragile replace in the sandbox. Node-primary projects keep Mode A (replace forst => … in the sandbox go.mod). For mixed .ft + hand-written .go you want to commit or inspect, use forst generate with generate.go.entry / generate.go.out (or --go-entry / --go-out) and the normal Go toolchain.
Examples: examples/in/go_interop/ (Go-native), examples/in/rfc/bridge-interop/remix-serve/ (Node-primary with .forst-gomod/go.mod).
Runtime validation
Type constraints in Forst emit runtime checks in generated Go. Untrusted input is validated at the boundary before your handler body executes. See Shapes and constraints.Providers
Forst Providers (use / with) declare shared runtime services at function boundaries and wire implementations at entry points. See Providers (DI).
See the provider examples on GitHub:
When to keep hand written Go
Forst supports gradual adoption. In some cases, you may need to keep hand-written.go when the compiler cannot yet type-check or lower a call site, or when your workflow needs committed Go next to generated output.
- Operations that require
unsafeor pointer arithmetic - Generic Go functions such as
slices.Containsormaps.Clone - Go interfaces such as
io.Readerorhttp.Handler - Same package
.gohelpers when running withforst run - Large existing packages or hot paths already tested in Go
Caveats
Go package importing is experimental. The compiler loads packages throughgo/packages and checks types when imports resolve. Many standard library paths work well. Full support for every Go package is still evolving.
Generic Go APIs
Functions with type parameters are rejected. Calls toslices.Contains or maps.Clone produce a go call diagnostic message. Full support requires generic type parameters in Forst.
Unsafe package
Qualified calls tounsafe.* and conversions to unsafe.Pointer are not supported. Put low level pointer code in hand written .go files.
Interface satisfaction
Forst shape types do not satisfy Go interfaces likeio.Reader or http.Handler automatically. Write a thin Go adapter or a type with explicit Go methods when a Go function expects an interface parameter.
Opaque structs and unexported fields
Go structs with unexported or embedded fields map toImplicit at the type boundary. You can call methods on tracked values. You cannot construct or inspect those internal fields directly inside Forst.
Function and method values
Passing or storing package functions without calling them directly is limited. Function literals passed as arguments work well, such ashttp.HandleFunc. Storing function values in variables can lose signature details.
Same package Go files with forst run
Theforst run command executes code in a temporary sandbox. It does not copy co-located .go files into that sandbox. Use forst generate along with go run or go test when mixing .ft and .go files in the same package.
Builtin make and new calls
Use Forst type syntax withmake and new. Write make(Array(Int), 10) or new(Int).
JSON field export
Shape fields lower to unexported Go names by default. Enable exported struct fields in configuration when you need JSON marshaling.Related
CLI reference
run, build, -root, and compile flags.Editor workflow
LSP hover for Go imports, godoc, and cross-package Forst siblings.
JavaScript overview
Share types, call Forst from Node, or call JavaScript from Forst.
Call JavaScript from Forst
Call legacy
.ts/.js from Forst at runtime.