This page contains examples demonstrating various features of the GALA language.
The following example demonstrates many of GALA’s features, including structs, immutability, expression functions, and control flow.
package main
struct Point(X, Y int)
func moveX(p Point, delta int) Point = p.Copy(X = p.X + delta)
func main() {
val p1 = Point(10, 20)
val p2 = moveX(p1, 5)
val msg = if (p2.X > 10) "moved" else "static"
Println(msg, p2)
}
Function calls support named arguments in any order. The compiler reorders them to match the function signature.
package main
func describe(name string, age int, role string) string =
s"$name ($role, age $age)"
func main() {
Println(describe("Alice", 30, "engineer")) // positional
Println(describe(role = "designer", name = "Bob", age = 25)) // named, any order
}
Functions can have default parameter values. Callers can omit trailing defaults or use named arguments to skip specific parameters.
package main
func greet(name string, greeting string = "Hello", punctuation string = "!") string =
s"$greeting, $name$punctuation"
func add(a int, b int = 10) int = a + b
func main() {
Println(greet("World")) // Hello, World!
Println(greet("World", "Hi")) // Hi, World!
Println(greet("World", "Hey", "...")) // Hey, World...
Println(greet("World", punctuation = "?")) // Hello, World?
Println(greet(name = "World", greeting = "Yo")) // Yo, World!
Println(add(5)) // 15
Println(add(5, 20)) // 25
}
Default expressions are evaluated at each call site:
package main
var counter = 0
func nextId() int {
counter = counter + 1
return counter
}
func createItem(name string, id int = nextId()) string =
s"$name#$id"
func main() {
Println(createItem("A")) // A#1
Println(createItem("B")) // B#2
Println(createItem("C", 99)) // C#99
}
package main
func main() {
val x = Some(10)
val y = x.Map((v) => v * 2) // parameter type inferred as int
val z = None().GetOrElse(42)
val res = y match {
case Some(v) => v
case _ => 0
}
Println(res, z)
}
See also: Language Reference - Option Monad
package main
type Box[T any] struct { Value T }
func (b Box[T]) Transform[U any](f func(T) U) Box[U] = Box[U](Value = f(b.Value))
func main() {
val b = Box[int](Value = 10)
val s = b.Transform((i) => s"Value is $i")
Println(s.Value)
}
See also: Language Reference - Generics
package main
struct Person(Name string, Age int)
func main() {
val people = SliceOf(
Person("Alice", 25),
Person("Bob", 15),
Person("Charlie", 70),
)
for _, p := range people {
val status = p match {
case Person(name, age) if age < 18 => name + " is a minor"
case Person(name, age) if age > 65 => name + " is a senior"
case Person(name, _) => name + " is an adult"
case _ => "Unknown"
}
Println(status)
}
}
See also: Language Reference - Pattern Matching Filters
package main
type Even struct {}
func (e Even) Unapply(i int) Option[int] = if (i % 2 == 0) Some(i) else None[int]()
func main() {
val number = 42
val description = number match {
case Even(n) => s"$n is an even number"
case _ => s"$number is odd"
}
Println(description)
// Nested patterns
val opt = Some(10)
opt match {
case Some(Even(n)) => Println("Found some even number", n)
case Some(n) => Println("Found some odd number", n)
case None() => Println("Nothing found")
case _ => Println("Other")
}
}
See also: Language Reference - Extractors and Unapply
package main
func main() {
val x any = "hello"
val res = x match {
case s: string => s"string: $s"
case i: int => s"int: $i"
case _ => "unknown"
}
Println(res)
}
package main
type Wrap[T any] struct { Value T }
func (w Wrap[T]) GetValue() any = w.Value
func main() {
val w = Wrap[string](Value = "hello")
val res = w match {
case w1: Wrap[_] => s"Matched Wrap[_]: ${w1.GetValue()}"
case _ => "Other"
}
Println(res)
}
package main
// Define a generic type
type Wrap[T any] struct {
Value T
}
// Define an extractor object
type Wrapper struct {}
func (w Wrapper) Apply[T any](v T) Wrap[T] = Wrap[T](Value = v)
func (w Wrapper) Unapply(o any) Option[any] = o match {
case wi: Wrap[_] => Some[any](wi.Value)
case _ => None[any]()
}
func main() {
// 1. Matching specific generic type
val w1 = Wrap[int](Value = 42)
val res1 = w1 match {
case w: Wrap[int] => s"Matched Wrap[int]: ${w.Value}"
case w: Wrap[string] => "Matched Wrap[string]: " + w.Value
case _ => "Other"
}
Println(res1)
// 2. Matching wildcard generic type
val w2 = Wrap[string](Value = "hello")
val res2 = w2 match {
case w: Wrap[_] => s"Matched Wrap[_]: ${w.Value}"
case _ => "Other"
}
Println(res2)
// 3. Using the generic extractor
val w3 = Wrapper("GALA")
val res3 = w3 match {
case Wrapper(s: string) => "Extracted string: " + s
case Wrapper(i: int) => s"Extracted int: $i"
case _ => "Other"
}
Println(res3)
}
package main
type Shaper interface {
Area() float64
}
struct Rect(width float64, height float64)
func (r Rect) Area() float64 = r.width * r.height
struct Circle(radius float64)
func (c Circle) Area() float64 = 3.14159 * c.radius * c.radius
func main() {
val r = Rect(10, 5)
val c = Circle(10)
val s1 Shaper = r
val s2 Shaper = c
Println("Area 1:", s1.Area())
Println("Area 2:", s2.Area())
}
See also: Language Reference - Interfaces
package main
type Adder struct { Delta int }
func (a Adder) Apply(x int) int = x + a.Delta
type Multiply struct {}
func (m Multiply) Apply(x int, y int) int = x * y
func main() {
val add5 = Adder(5)
val res1 = add5(10) // 15
val res2 = Multiply(3, 4) // 12
Println(res1, res2)
}
GALA allows you to organize your code into multiple packages and import them as needed.
mathlib/math.gala
package mathlib
func Add(a int, b int) int = a + b
main.gala
package main
import "martianoff/gala/examples/mathlib"
func main() {
val sum = mathlib.Add(10, 20)
Println("Sum is", sum)
}
You can also use dot imports to bring symbols into the current namespace:
import . "martianoff/gala/examples/mathlib"
func main() {
val sum = Add(10, 20)
Println("Sum is", sum)
}
See also: Language Reference - GALA Packages, Dependency Management
GALA uses the Hindley-Milner algorithm to infer types in complex scenarios, such as generic function calls. This enables features like automatic unwrapping of Immutable values even when they are returned from generic functions.
package main
func identity[T any](x T) T = x
func getImm[T any](x T) Immutable[T] = NewImmutable(x)
func main() {
// HM infers Immutable[int], which GALA then automatically unwraps to int
var x = identity(getImm(42))
Println(s"x: $x")
}
Boolean pattern matching is exhaustive when both true and false cases are covered:
val desc = flag match {
case true => "enabled"
case false => "disabled"
// No case _ needed — true/false is exhaustive
}
Functions can accept void closures (no return value), and lambda parameter types can be inferred from context. Prefer omitting parameter types in lambdas — the compiler infers them from the method signature:
val opt = Some(42)
// ForEach takes func(T) — void, no return needed
opt.ForEach((x) => {
Println(x)
})
// Parameter type inferred from Option[int].Filter's signature
val positive = opt.Filter((x) => x > 0)
// Method type param and lambda param both inferred
val doubled = opt.Map((x) => x * 2)
// FoldLeft accumulator type inferred from zero value
val list = ArrayOf(1, 2, 3)
val sum = list.FoldLeft(0, (acc, x) => acc + x)
// Non-generic wrapper methods also infer lambda param types
val s = S("hello")
val hasVowel = s.Exists((r) => r == 'a' || r == 'e' || r == 'i' || r == 'o' || r == 'u')
Collect combines Filter and Map into a single operation using partial functions:
package main
import . "martianoff/gala/collection_immutable"
func main() {
val nums = ArrayOf(1, 2, 3, 4, 5, 6)
// Collect: filter and transform in one pass
val evenDoubled = nums.Collect({ case n if n % 2 == 0 => n * 2 })
Println(evenDoubled) // Array(4, 8, 12)
// With sealed type extractors
val options = ArrayOf(Some(1), None[int](), Some(2), None[int](), Some(3))
val values = options.Collect({ case Some(v) => v * 10 })
Println(values) // Array(10, 20, 30)
}
See also: Immutable Collections
package main
func main() {
val primary = None[string]()
val fallback = Some("default")
val result = primary.OrElse(fallback) // Some("default")
Println(result.Get()) // "default"
val present = Some("actual")
val result2 = present.OrElse(fallback) // Some("actual")
Println(result2.Get()) // "actual"
}
When wrapping a zero-argument function, pass the function reference directly instead of wrapping in a lambda:
package main
import "os"
func main() {
// Function reference (preferred for zero-arg functions)
val dir = Try(os.TempDir)
// Lambda form (use when args needed)
val result = Try(() => os.MkdirAll("/tmp/test", 0755))
dir.OnSuccess((d) => Println(s"Temp dir: $d"))
}
package main
import . "martianoff/gala/collection_immutable"
func main() {
val nums = ArrayOf(1, 2, 3, 4, 5)
Println(nums.MkString(", ")) // "1, 2, 3, 4, 5"
Println(nums.MkString(" | ")) // "1 | 2 | 3 | 4 | 5"
val words = ListOf("hello", "world")
Println(words.MkString(" ")) // "hello world"
val empty = EmptyArray[int]()
Println(empty.MkString(", ")) // ""
}
All collection types support Sorted(), SortWith(), and SortBy() for flexible sorting:
package main
import . "martianoff/gala/collection_immutable"
func main() {
// Array.Sorted - natural ordering
val arr = ArrayOf(3, 1, 4, 1, 5, 9)
Println(arr.Sorted()) // Array(1, 1, 3, 4, 5, 9)
// Array.SortWith - custom comparator (descending)
Println(arr.SortWith((a, b) => a > b)) // Array(9, 5, 4, 3, 1, 1)
// Array.SortBy - sort by key function
val arr2 = ArrayOf(1, 9, 3, 7, 5)
Println(arr2.SortBy((x) => x)) // Array(1, 3, 5, 7, 9)
// List.Sorted
val list = ListOf("banana", "apple", "cherry")
Println(list.Sorted()) // List(apple, banana, cherry)
// TreeSet.Sorted (already sorted)
val tree = TreeSetOf(30, 10, 20)
Println(tree.Sorted()) // Array(10, 20, 30)
// HashSet.Sorted
val set = HashSetOf(5, 3, 1)
Println(set.Sorted()) // Array(1, 3, 5)
// Empty collection
Println(EmptyArray[int]().Sorted()) // Array()
}
See also: Immutable Collections, Mutable Collections
Type aliases create alternative names for existing types, useful for re-exporting or shortening names:
package main
type MyString string
func main() {
val s MyString = "hello"
Println(s)
}
Output:
hello
You can find more examples in the examples/ directory of the project:
complex.gala: A more complex example showing pattern matching and init() function.option_complex.gala: Demonstrates advanced usage of the Option monad (Map, FlatMap, Filter).hello.gala: A simple “Hello, World!” example.with_main.gala: An example with a main function.imports.gala: Demonstrates importing standard Go packages with aliases and dot imports.use_lib.gala: Demonstrates importing another GALA package (mathlib).tuple.gala: Demonstrates using Tuple[A, B] with pattern matching.either.gala: Demonstrates using Either[A, B] with monadic operations and pattern matching.either_map_flatmap.gala: Demonstrates Either.Map and Either.FlatMap chaining.unary_minus.gala: Demonstrates unary operators (-, !).sealed_wildcard.gala: Demonstrates wildcard case _ => catch-all in sealed type matching.sorted.gala: Demonstrates Sorted(), SortWith(), and SortBy() on collections.| See also: Language Reference | Why GALA? | Immutable Collections | Streams |