On this page
Vo uses Go-shaped syntax, so declarations and control flow will look familiar. Existing Go source should be treated as a port: Vo deliberately has different type, module, error, pointer, dynamic-access, and concurrency semantics.
If you know Go, start with these four visible differences and consult the language specification when porting code:
- Error Handling: Use
?instead ofif err != nil. Useerrdeferfor error-only cleanup. - No Generics: Use
any(interface{}) and type assertions. - Restricted Pointers: Only structs can be pointers (
*User). No*intor*string. - Dynamic Access: Use
~>operator for duck-typing (JSON, maps, untyped data).
Familiar surface#
Packages, imports, short declarations, structs, methods, interfaces, slices,
maps, if, for, range, switch, defer, goroutines, channels, and
select intentionally look familiar:
package main
import "fmt"
type Counter struct {
Value int
}
func (counter *Counter) Add(delta int) {
counter.Value += delta
}
func main() {
counter := &Counter{}
for _, value := range []int{1, 2, 3} {
counter.Add(value)
}
fmt.Println(counter.Value)
}
Treat this familiarity as a reading advantage. Type identity, pointer eligibility, module selection, runtime scheduling, host effects, and release artifacts follow Volang's specifications.
Error Handling#
Think of ? as if err != nil { return err } but inline. It unwraps the value or immediately returns the error.
expr?: Propagates error, evaluates to value on success.errdefer: Cleanup that runs only on error return (like rollback). Standarddeferruns always.fail err: Explicitly return an error.
user := allocUser()? // Returns err if alloc fails
errdefer deleteUser(user) // Cleanup only if later steps fail
save(user)? // If this fails, deleteUser runs
An enclosing function using ?, fail, or errdefer must have the
predeclared error type as its final result. On propagation, ordinary defers
and eligible error defers share one reverse registration order.
func replace(path string, data []byte) error {
temporary := createTemporary(path)?
errdefer os.Remove(temporary)
file := os.Create(temporary)?
defer file.Close()
file.Write(data)?
activate(temporary, path)?
return nil
}
Use returned errors for operational failures. Reserve panic/recover for invariants and deliberate unwind boundaries.
Pointers and value categories#
Volang permits pointers only to struct base types. *int, pointer arithmetic,
and arbitrary address manipulation are outside the language.
type User struct {
Name string
}
func (user *User) Rename(name string) {
user.Name = name
}
Struct values copy on assignment. Struct pointers, slices, maps, interfaces, functions, channels, ports, and islands carry reference or capability semantics. Consult the language and runtime-memory specifications before porting code whose correctness depends on aliasing or object lifetime.
Interfaces and generics#
Interfaces are satisfied structurally through method sets. Volang currently has no generic declarations or instantiations. Prefer a small behavior interface when callers need abstraction, and a concrete typed operation when they need performance and static result types.
Open data can use any, type switches, or the dynamic protocol. Avoid replacing
every generic Go helper with any; collection-specific functions are often
clearer and preserve checking.
Dynamic Access (~>)#
Don't use reflection or verbose type assertions for any (interface{}) or JSON. Use the ~> operator.
v, err := data~>fieldreturns(any, error).v := data~>field?propagates error AND automatically casts result to your variable's type.
It works for struct fields, map keys (data~>[key]), and method calls (data~>Method()).
// data is interface{} (e.g. parsed JSON)
var name string
name = data~>users~>[0]~>name? // Access path, auto-cast to string, or return err
Dynamic operations return stable error categories for unsupported operations, missing members, bad indexes, and type mismatches. They remain bounded protocol dispatch rather than unrestricted reflection.
Modules and imports#
Volang does not consume go.mod or the Go module cache. A project owns TOML
intent in vo.mod and one exact authenticated graph in vo.lock:
format = 1
module = "example.com/acme/product"
version = "0.1.0"
vo = "0.1.4"
[dependencies]
"github.com/acme/records" = "^1.2.0"
Imports contain the selected ModuleId without a version. Run vo mod sync to
select, vo mod fetch to materialize selected bytes, and vo mod verify to
check the frozen graph. A compiler invocation never changes dependency intent.
Workspaces explicitly select local module origins in the lock. Nested workspaces do not merge.
Goroutines, channels, and islands#
Goroutines and channels coordinate inside one island. An island owns a VM, heap, scheduler, collector, and host-capability boundary. Channels and heap references stay inside that boundary.
Use typed ports for cross-island communication. Native hosts may place islands on different threads; browser hosts may place them in separate Wasm instances or workers. Messages are encoded and validated, so serialization, backpressure, cancellation, and failure are visible design decisions.
Go code that relies on preemptive scheduling, shared pointers across all goroutines, or implicit host-wide blocking behavior needs an explicit Volang concurrency design. Pass contexts, own goroutine lifetimes, and reject stale interactive completions with a generation check.
Development and release modes#
Go normally compiles before execution. Volang supports several modes over the same verified semantics:
- VM for short startup and embedding;
- Cranelift JIT for longer native development sessions;
- Native AOT for standalone executables or objects;
- Wasm VM for browser and sandboxed Wasm hosts.
Use vo check, vo test --mode=vm, and vo test --mode=jit during a port.
Publish with vo build or the official UI release commands after backend
parity tests pass.
Porting checklist#
- Create a canonical
vo.modidentity and select dependencies explicitly. - Replace generic APIs with concrete functions, small interfaces, or governed dynamic values.
- Replace unsupported pointer shapes and audit value-copy assumptions.
- Convert repetitive propagation to
?and rollback cleanup toerrdefer. - Separate island-local channels from cross-island port messages.
- Replace direct platform authority with standard-library or host capability contracts that work on the target backends.
- Run formatting, checking, VM tests, JIT tests, and the intended AOT release target before comparing performance.