Skip to content

Axon for Go Developers

If you are coming from Go, you already appreciate most of what Axon is trying to do: simple syntax, fast compilation, native binaries, and serious concurrency primitives. The differences are mostly about how those goals are achieved.

Both Go and Axon produce small, statically-linked native binaries from a simple source layout. Both compile fast (Go: a few seconds; Axon: similar). Both have built-in testing tools. Both prioritize readability over expressiveness.

GoAxon
package main(module ...)
import "fmt"(import fmt "std/fmt.axs")
func main() { ... }(fn main () i32 ...)
func Add(a, b int) int(fn add ((a i64) (b i64)) i64 ...)
type Point struct { X, Y int }(struct Point ((x i64) (y i64)))
var x int = 5(let (x i64) (i64 5))
for i := 0; i < n; i++ {}(for ((i i64)) (range (i64 0) n (i64 1)) ...)
go func() { ... }(go (fn () ...))
ch := make(chan int)(let (ch (call chan_new (i64 16))))
defer f.Close()not directly supported; use explicit cleanup on every return path
if err != nil { ... }(if (is_err err) (block ...)) — errors are explicit return values
  • Goroutines vs green threads. Go has a full M:N runtime scheduler that multiplexes goroutines onto OS threads. Axon’s (go ...) is cooperative — the scheduled function runs until it explicitly yields via await, chan_recv, or evloop_wait. This makes Axon’s runtime tiny (no scheduler overhead) but requires that hot loops yield periodically.
  • Channels work the same way. Both Go and Axon provide unbuffered/buffered channels, send/recv, select. The Axon API is lower-level (no select keyword yet) but the semantics are identical.
  • Errors are values, both languages. Go has (T, error). Axon has (Result T E) from std/result. Same idea.
  • No garbage collector in Axon. Go has a concurrent GC (≈1ms pauses). Axon requires explicit (alloc T) / (free ptr). This trades Go’s runtime cost for Axon’s zero-pause determinism.
  • No interface satisfaction in Axon. Go uses structural typing (type Reader interface { Read(p []byte) (n int, err error) }). Axon uses nominal typing with traits ((trait Readable ((self T) (read (ptr u8) i64) i64))).
  • No build tags or conditional compilation. Axon has one binary per target triple; platform-specific code uses traits and the dispatcher.

A simple web handler:

Go:

package main
import (
"encoding/json"
"net/http"
)
type Greeting struct {
Message string `json:"message"`
}
func main() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(Greeting{Message: "Hello, World!"})
})
http.ListenAndServe(":8080", nil)
}

Axon:

(import std/http)
(import std/json)
(struct Greeting ((message string)))
(fn main () i32
(call http.serve (i64 8080)
(fn ((req (ptr http.Request)) (resp (ptr http.Response)))
(set resp.body
(call json.encode (Greeting (str "Hello, World!"))))
(i32 0))))
(i32 0))

The Axon version is longer because every callback, allocation, and error path is explicit. The runtime is identical (kernel-level epoll loop), the binary is ~30% smaller, and there is no GC pause.

PatternGoAxon
Spawn a taskgo func() {...}()(go (fn () ...))
Wait for completionwg.Wait()(join handle)
Channel sendch <- val(call chan_send ch val)
Channel recvval := <-ch(let (val (call chan_recv ch)) ...)
Non-blocking tryselect { case ch <- v: default: ... }(if (eq (call chan_try_send ch v) (i64 0)) ...)
Mutexvar mu sync.Mutex; mu.Lock(); defer mu.Unlock()(let (m (call mutex_new))) (call mutex_lock m) ; ... (call mutex_unlock m)
Cancelctx, cancel := context.WithCancel(...)not built-in; use actor message + poison pill
Worker poolfor i := 0; i < N; i++ { go worker(ch) }spawn N, join all

Stay in Go for:

  • Microservices that fit in the standard library’s “batteries included” model
  • Codebases that depend heavily on the Go ecosystem (gRPC, kubernetes, prometheus clients)
  • Teams that prefer goroutines + channels and want runtime scheduling for free

Reach for Axon for:

  • Sub-millisecond-latency services (HFT, gaming servers, embedded)
  • Performance-critical paths where Go’s GC pauses are unacceptable
  • Code that needs to compile to WebAssembly for browser deployment
  • AI-generated services where the simpler S-expression syntax reduces hallucinations
  • Syntax: S-expressions instead of C-like syntax with mandatory braces.
  • Concurrency: Cooperative green threads (you yield) instead of preemptive goroutines (runtime decides).
  • Memory: Explicit (alloc) / (free) instead of concurrent GC.
  • Errors: (Result T E) value type instead of (T, error) tuples.
  • Standard library: Smaller than Go’s stdlib but covers the same primitives (HTTP, channels, mutexes, time, JSON, hashmap).