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 and type-checks Forst↔Go calls when imports resolve. Supported mappings today include primitives, slices, pointers, error, and interface{} (including variadic calls).
Go functions that return (T, error) map to Result(T, Error) in Forst at call sites. That applies to the result type of the call and to trailing expressions in implicit returns. This differs from using println / print for void side effects inside a function that should not return a value.
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 (task example:slices). 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 (task example:go-interop).
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).
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.
Try the cross-package provider examples:
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 all same-package sources under a tree, matching discovery for forst run, forst build, and the dev server:
.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 writes a temp sandbox under .forst/run/ with its own go.mod and links the forst runtime module. 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, prefer forst build and the normal Go toolchain — forst run emits transpiled .go only.
Examples: examples/in/go_interop/ (Go-native), examples/in/rfc/node-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).
Run the provider examples:
When to keep hand written Go
unsafeor low-level syscalls not yet mapped- Large existing packages you are migrating incrementally
- Performance-critical paths already audited in Go
Caveats
Go import loading is experimental. Unmapped Go types report diagnostics. This is not a full Go loader yet.unsafe not end to end
Put unsafe usage in hand written .go and call from Forst. Qualified unsafe.* calls and unsafe.Pointer mapping are not wired through the Forst import path yet.
make and new type args
make and new with a type argument are rejected until Forst type syntax works in that position (e.g. Array(Int), not Go []T).
Slice subslices experimental
Three index subslice syntax on Forst[]T values is experimental. It is distinct from variadic spread at Go call sites.
JSON field export
Shape fields lower to unexported Go identifiers by default. For JSON marshaling, see Shapes and constraints § Caveats.Related
CLI reference
run, build, -root, and compile flags.Editor workflow
LSP hover for Go imports, godoc, and cross-package Forst siblings.
Node overview
Share types, call Forst from Node, or call JavaScript from Forst.
Call JavaScript from Forst
Call legacy
.ts/.js from Forst at runtime.