Skip to content
tomaszbkPublic
forked from golang/go

About

Gon extends Go natively with concise errors, optionals, enums, exhaustive matching, lambdas and more. Go-compatible, with compiler and editor tooling.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

Gon mascot

Gon — Another way to Go!

Less boilerplate. Go-compatible.

See the difference · Examples · Documentation · Try Gon · Editor setup · Support Gon

The Go features we always wanted, minus the boilerplate we always hated.

Gon extends the Go compiler directly. Use Gon to build, test and run your project. This project is NOT a transpiler.

See the difference

Error propagation.

Read a file, add context if it fails, then parse it.

Go

func loadConfig(path string) (Config, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return Config{}, fmt.Errorf("read config %q: %w", path, err)
	}
	config, err := parseConfig(data)
	if err != nil {
		return Config{}, err
	}
	return config, nil
}

Gon

func loadConfig(path string) (Config, error) {
	data := os.ReadFile(path) or err => fmt.Errorf("read config %q: %w", path, err)
	config := parseConfig(data)!
	return config, nil
}

! returns early on failure; or err { ... } lets you handle the error yourself. Both work with existing Go error-returning APIs. or err => expression adds context and propagates the resulting error.

Lambdas · null-safety operators · conditional expressions · named arguments.

Sort users, choose a title and label, then format the list. The helper has the ordinary signature formatList(title string, count int, unit string) string.

Go

func userList(users []User, owner *User) string {
	slices.SortFunc(users, func(a, b User) int {
		return cmp.Compare(a.Name, b.Name)
	})
	title := "guest"
	if owner != nil {
		title = owner.Name
	}
	unit := "users"
	if len(users) == 1 {
		unit = "user"
	}
	return formatList(title, len(users), unit)
}

Gon

func userList(users []User, owner *User) string {
	slices.SortFunc(users, (a, b) => cmp.Compare(a.Name, b.Name))
	title := owner?.Name ?? "guest"
	unit := if len(users) == 1 { "user" } else { "users" }
	return formatList(title: title, count: len(users), unit: unit)
}

The callback gets its types from context. ?. guards a nil owner, ?? supplies a fallback, and the expression if selects a value. Labels use the function's existing parameter names.

Enums · pattern matching.

A payment has one active state, with data that belongs to that state. Handle each alternative without manually coordinating an integer tag and unrelated fields.

Go

type paymentState int

const (
	pending paymentState = iota
	paid
	rejected
)

type Payment struct {
	state   paymentState
	Receipt string
	Reason  string
}

func describePayment(payment Payment) string {
	switch payment.state {
	case pending:
		return "pending"
	case paid:
		return "paid: " + payment.Receipt
	case rejected:
		return "rejected: " + payment.Reason
	default:
		panic("invalid payment state")
	}
}

Gon

type Payment enum {
	default Pending
	Paid { Receipt string }
	Rejected(string)
}

func describePayment(payment Payment) string {
	return switch payment {
	case Payment.Pending => "pending"
	case Payment.Paid{Receipt: receipt} => "paid: " + receipt
	case Payment.Rejected(reason) => "rejected: " + reason
	}
}

Gon checks matches for exhaustiveness: adding a state flags missing cases. Ordinary Go switches keep their rules.

String enums preserve unknown text and support JSON and database/sql directly. See the string-enum contract.

Enum patterns also match interfaces and wrapped errors; a fallback is required. See matching interface and error subjects.

Optional values (T?) · error returns.

Empty input means no port; invalid input means an error. Zero is still a value.

Go

func parsePort(text string) (int, bool, error) {
	if text == "" {
		return 0, false, nil
	}
	port, err := strconv.Atoi(text)
	if err != nil {
		return 0, false, err
	}
	return port, true, nil
}

func portLabel(text string) string {
	port, found, err := parsePort(text)
	if err != nil {
		return "invalid port"
	}
	if !found {
		port = 8080
	}
	return strconv.Itoa(port)
}

Gon

func parsePort(text string) (port int?, err error) {
	if text == "" {
		return nil, nil
	}
	number := strconv.Atoi(text)!
	return number, nil
}

func portLabel(text string) string {
	port := parsePort(text) or err {
		return "invalid port"
	}
	return strconv.Itoa(port ?? 8080)
}

int? means an optional integer: nil is absent, while an integer, including zero, is present. The ordinary (int?, error) return keeps absence and failure separate: (nil, nil) has no setting, (number, nil) has one, and an error reports a failed parse.

Input Gon return Display
"443" 443, nil "443"
"" nil, nil "8080"
"0" 0, nil "0"
"abc" nil, err "invalid port"

or handles failure; ?? defaults only on absence. Existing Go error returns also retain useful partial results when callers handle them explicitly.

T? is the native optional type. Match presence with case value? and absence with case nil. A typed nil can be present; nested optionals retain their individual layers. Optionals map natively to JSON null and SQL NULL in encoding/json and database/sql. See native optionals.

More examples and boundary cases → · Optional types and ordinary errors →

More in Gon 2.27

The eight core features above also work with these additions:

Addition What it provides Documentation
Alternative patterns Several patterns in one arm: case A, B => Patterns
Pattern tests value is pattern, with bindings in if and later && operands Examples
Error context call() or err => fmt.Errorf(...) Error handling
String interpolation $"Hello ${name}", including ${price:%.2f} Interpolation
Collections Generic gon/seq functions, including optional lookups Collections
Nil analysis Opt-in NilAway warnings in gon check and gonpls NilAway

String enums supply text, JSON and SQL conversions. Native optionals support JSON and SQL NULL, and database/sql provides strict struct scanning.

Documentation

Start with Gon by example: executable Go/Gon comparisons cover the language features, success, errors, absence and evaluation order.

Try Gon

Download a prebuilt toolchain from Gon releases and install the Gon extension from the VS Code Marketplace. The release also includes a VSIX for manual installation. Installation instructions →

Use the Gon 2.27.0 container image to compile and test Gon projects or build application containers for the cloud:

docker run --rm ghcr.io/tomaszbk/gon:v2.27.0 gon version

Build from this checkout with an unmodified bootstrap Go toolchain:

cd src
GOROOT_BOOTSTRAP=/absolute/path/to/bootstrap/go-root ./make.bash
cd ..
python3 misc/gon/build.py
./gon/bin/gon version

GOROOT_BOOTSTRAP is the directory of an unmodified Go 1.27+ toolchain. Build Gon once and keep its toolchain directory in place.

Use Gon in an existing Go project

Download and install Gon, set go 2.27 in your project's go.mod, then build and test:

cd /path/to/your-project
gon test ./...
gon build ./...

go 2.27 requires Gon 2.27 or newer. Existing go 1.x modules keep working. Use gon for your editor and CI too.

For VS Code, install the Gon extension, disable the Go extension for this workspace, and reload.

Status and compatibility

Gon 2.27 supports Go 1.27+ source. Gon 2.28 will follow the release of Go 1.28.

Performance, measured

In our measurements, rebuild times were comparable and Gon binaries were 1.7% smaller.

Measurement Go 1.27.1 Gon, legacy Go Gon, modern code
Recompile and link, dependencies cached 249 ms 249 ms 251 ms
Build without changes 105 ms 109 ms 115 ms
Executable size, bytes 3,819,122 3,752,418 3,752,466

Support Gon

If Gon helps you, consider supporting its development through GitHub Sponsors.

Upstream and license

Gon is a native fork of Go, with upstream copyright notices and license preserved. Source and tooling provenance.

About

Gon extends Go natively with concise errors, optionals, enums, exhaustive matching, lambdas and more. Go-compatible, with compiler and editor tooling.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages