GALA is not replacing Go – it is adding expressiveness on top of it. GALA transpiles to standard Go code, so you get the same runtime, the same libraries, the same single-binary deployments, and the same performance. The difference is what you write to get there.
This page shows real code comparisons between GALA and the equivalent idiomatic Go. Every GALA snippet on this page uses only syntax from the language specification.
GALA sealed types define closed hierarchies. The compiler enforces exhaustive matching – if you forget a case, it will not compile. Go requires manual variant tracking with constants and field accessors.
GALA
sealed type Shape {
case Circle(Radius float64)
case Rectangle(Width float64, Height float64)
case Point()
}
val msg = shape match {
case Circle(r) => f"r=$r%.1f"
case Rectangle(w, h) => f"$w%.0fx$h%.0f"
case Point() => "point"
}
Go
var msg string
switch shape._variant {
case Shape_Circle:
msg = fmt.Sprintf("r=%.1f",
shape.Radius.Get())
case Shape_Rectangle:
msg = fmt.Sprintf("%fx%f",
shape.Width.Get(),
shape.Height.Get())
case Shape_Point:
msg = "point"
}
GALA destructures values directly into named bindings (r, w, h) and produces a compile-time error if you forget a case. Go’s switch does not enforce exhaustiveness and requires explicit field access through .Get() calls.
GALA’s Option[T] type makes the presence or absence of a value explicit. You chain operations with Map, FlatMap, and GetOrElse instead of checking for nil.
GALA
val name = user.Name
.Map((n) => strings.ToUpper(n))
.GetOrElse("ANONYMOUS")
Go
name := "ANONYMOUS"
if user.Name != nil {
name = strings.ToUpper(*user.Name)
}
With Option[T], you never forget a nil check. The type system enforces it. You can also pattern match on options directly:
val res = opt match {
case Some(v) => s"got $v"
case None() => "nothing"
}
GALA structs are immutable by default. Fields cannot be reassigned after construction. The compiler auto-generates a Copy() method for creating modified copies, and an Equal() method for structural comparison.
GALA
struct Config(Host string, Port int)
val updated = config.Copy(Port = 8080)
Go
type Config struct {
Host string
Port int
}
updated := Config{
Host: config.Host,
Port: 8080,
}
In Go, you must manually copy every field you want to keep. With GALA’s Copy(), you only specify the fields that change. As your struct grows, the GALA version stays the same size while the Go version grows with every field.
GALA’s Try[T] type wraps computations that can fail. Instead of checking if err != nil after every call, you chain operations with Map, FlatMap, and Recover.
GALA
val result = divide(10, 2)
.Map((x) => x * 2)
.FlatMap((x) => divide(x, 3))
.Recover((e) => 0)
Go
result, err := divide(10, 2)
if err != nil {
result = 0
} else {
result = result * 2
result, err = divide(result, 3)
if err != nil {
result = 0
}
}
The GALA version reads as a linear pipeline: divide, double, divide again, recover on failure. The Go version nests deeper with each operation. Both compile to equivalent logic, but GALA expresses the intent more clearly.
You can also pattern match on Try[T]:
val msg = result match {
case Success(v) => s"got $v"
case Failure(e) => s"error: $e"
}
GALA provides immutable functional collections with Map, Filter, FoldLeft, Collect, and more. Go requires manual loops with append.
GALA
val nums = ArrayOf(1, 2, 3, 4, 5)
val result = nums
.Filter((x) => x % 2 == 0)
.Map((x) => x * 2)
Go
nums := []int{1, 2, 3, 4, 5}
var result []int
for _, x := range nums {
if x%2 == 0 {
result = append(result, x*2)
}
}
GALA’s Collect combines filter and transform in a single pass using partial functions:
val evenDoubled = nums.Collect({ case n if n % 2 == 0 => n * 2 })
Other collection operations:
val sum = nums.FoldLeft(0, (acc, x) => acc + x)
val sorted = nums.SortWith((a, b) => a > b)
val csv = nums.MkString(", ")
GALA has built-in string interpolation with s"..." for auto-inferred format verbs and f"..." for explicit format specs. No imports needed.
GALA
val name = "Alice"
val age = 30
Println(s"$name is $age years old")
Println(f"Pi = ${3.14159}%.2f")
Go
name := "Alice"
age := 30
fmt.Printf("%s is %d years old\n", name, age)
fmt.Printf("Pi = %.2f\n", 3.14159)
GALA also provides Println and Print as built-in functions – no fmt import required.
Go has no default parameters. The common workaround is the “functional options” pattern, which requires a struct, variadic functions, and option types. GALA adds defaults directly to the function signature.
GALA
func connect(
host string,
port int = 8080,
tls bool = true,
) Connection {
// ...
}
connect("localhost")
connect("db", tls = false)
Go
type ConnectOption func(*connectOpts)
type connectOpts struct {
port int; tls bool
}
func WithPort(p int) ConnectOption { ... }
func WithTLS(t bool) ConnectOption { ... }
func Connect(host string,
opts ...ConnectOption,
) Connection { ... }
Connect("localhost")
Connect("db", WithTLS(false))
Named arguments let callers skip parameters with defaults. The compiler validates default types at compile time and gives clear errors for mismatches.
GALA is a strong fit when you want:
Option[T], Either[A,B], Try[T] instead of if err != nilval bindings and auto-generated Copy() / Equal()Map, Filter, FoldLeft, Collect on immutable data structuresGo is the better choice when you need:
go buildBoth are valid choices. GALA and Go produce the same binaries and use the same runtime – the difference is in the source language you write.
GALA uses Go libraries directly. There are no wrappers, no bindings, and no FFI layer. If a Go package exists, you can import it and call it from GALA:
import "strings"
import "os"
val upper = strings.ToUpper("hello")
val dir = Try(os.TempDir)
Go types, interfaces, and functions are all available. GALA adds its own type system on top – sealed types, Option[T], immutable structs – but the underlying Go interop is seamless.
Option[T], Either[A,B], Try[T] monads