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 int, 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[int]().GetOrElse(42)
val res = y match {
case Some(v) => v
case _ => 0
}
Println(res, z)
}
See also: Language Reference - Option Monad
A sealed type’s default case is its zero value: a field a struct literal
leaves out holds it. Option’s default case is None().
package main
sealed type Mode {
case On(Level int)
default case Off()
}
struct Lamp(Name string, M Mode, Dim Option[int])
func describe(l Lamp) string = l.M match {
case On(level) => s"${l.Name} on at $level"
case Off() => s"${l.Name} off, dim ${l.Dim.GetOrElse(0)}"
}
func main() {
Println(describe(Lamp{Name: "desk"}))
Println(describe(Lamp("hall", On(7), Some(3))))
}
Output:
desk off, dim 0
hall on at 7
A case’s fields can declare defaults. A constructor leaves them out by position or by name.
package main
sealed type Light {
case Off()
default case On(Power int = 30, Room string = "Living")
}
func main() {
Println(On(), On(Power = 50), On(10, "Kitchen"))
}
Output:
On(30, Living) On(50, Living) On(10, Kitchen)
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 Counter(var N int)
// Writing a `var` field through the receiver is allowed; rebinding it is not.
func (c *Counter) Bump() {
c.N = c.N + 1
}
func main() {
var c = Counter(0)
c.Bump()
// A lambda parameter is reassignable only when declared `var`.
val clamp = (var n int) int => {
if (n < 0) {
n = 0
}
n
}
Println(c.N, clamp(-3))
}
package main
import . "martianoff/gala/collection_immutable"
struct Person(Name string, Age int)
func main() {
val people = ArrayOf(
Person("Alice", 25),
Person("Bob", 15),
Person("Charlie", 70),
)
people.ForEach((p) => {
val status = p match {
case Person(name, age) if age < 18 => s"$name is a minor"
case Person(name, age) if age > 65 => s"$name is a senior"
case Person(name, _) => s"$name is an adult"
case _ => "Unknown"
}
Println(status)
})
}
See also: Language Reference - Pattern Matching Filters
package main
sealed type Event {
case Click(X int, Y int)
case Tap(X int, Y int)
case Key(Code int)
case Quit()
}
func describe(e Event) string = e match {
case Click(_, _) | Tap(_, _) => "pointer"
case Key(9 | 13 | 32) => "whitespace key"
case Key(_) => "other key"
case Quit() => "quit"
}
func main() {
Println(describe(Tap(1, 2)))
Println(describe(Key(13)))
Println(describe(Key(65)))
Println(describe(Quit()))
}
package main
// Named sub-patterns: `Field = p` matches p against the field of that name.
// Fields left out match anything.
sealed type Shape {
case Circle(Radius int)
case Rect(Width int, Height int)
}
struct Point(X int, Y int)
func height(s Shape) int = s match {
case Circle(Radius = r) => 2 * r
case Rect(Height = h, Width = _) => h
}
func flat(s Shape) bool = s match {
case Rect(Height = 0) => true
case _ => false
}
func square(s Shape) string = s match {
case Rect(1, Height = 1) => "unit square"
case Rect(w, Height = h) if w == h => "square"
case Rect(_, _) => "rect"
case Circle(_) => "circle"
}
func axis(p Point) string = p match {
case Point(Y = 0) => "on the x axis"
case Point(X = 0) => "on the y axis"
case _ => s"(${p.X}, ${p.Y})"
}
func main() {
Println(height(Rect(1, 2)), height(Circle(3)))
Println(flat(Rect(5, 0)), flat(Rect(0, 5)))
Println(square(Rect(1, 1)), square(Rect(2, 2)), square(Rect(2, 3)), square(Circle(1)))
Println(axis(Point(4, 0)), axis(Point(0, 4)), axis(Point(1, 2)))
}
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
A type with MatchView() S, S sealed, is matched with S’s variants:
package main
sealed type Level {
case Low()
case Mid(N int)
case High()
}
struct Reading(v int)
func (r Reading) MatchView() Level = if (r.v < 10) Low() else if (r.v < 100) Mid(r.v) else High()
func level(r Reading) string = r match {
case Low() | High() => "edge"
case Mid(n) => s"mid $n"
}
func main() {
Println(level(Reading(1)), level(Reading(42)))
}
See also: Language Reference - Sealed Views
a == b calls a.Equal(b) on structs, sealed types, tuples, Option and the collections, so values built separately with the same contents are ==. Types made of primitives only keep Go’s ==, which gives the same answer.
package main
import . "martianoff/gala/collection_immutable"
struct Route(name string, stops List[string])
val Home = ListOf("home")
func where(stops List[string]) string = stops match {
case Home => "home"
case _ => "away"
}
func main() {
Println(ListOf(1, 2) == ListOf(2).Prepend(1)) // true
Println(Route("r", ListOf("a")) == Route("r", ListOf("a"))) // true
Println(HashSetOf(ArrayOf(1, 2), ArrayOf(1).Append(2)).Size()) // 1
Println(where(ListOf("home"))) // home
}
A capitalized name that refers to a value in scope compares with that value instead of binding a new variable. A lowercase name never compares with a value: it binds (unless it names a variant of the matched sealed type or a zero-field extractor), so compare against a lowercase value with a guard.
package main
type Environment string
val Development Environment = "development"
val Production Environment = "production"
val Answer = 42
func describe(env Environment) string = env match {
case Development => "dev"
case Production => "prod"
case _ => s"other: $env"
}
func probe(o Option[int]) string = o match {
case Some(Answer) => "the answer"
case Some(n) => s"some $n"
case _ => "none"
}
func main() {
Println(describe(Production)) // prod
Println(probe(Some(42))) // the answer
val limit = 10
Println(10 match {
case n if n == limit => "at the limit"
case n => s"bound $n"
}) // at the limit
}
See also: Language Reference - Stable Identifiers
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.0, 5.0)
val c = Circle(10.0)
val s1 Shaper = r
val s2 Shaper = c
Println("Area 1:", s1.Area())
Println("Area 2:", s2.Area())
}
See also: Language Reference - Interfaces
An interface literal builds an interface value from one function per method, without declaring a struct. Tests use it to fake dependencies.
package main
type Clock interface {
Now() int
Zone() string
}
struct Event(Name string, clock Clock)
func (e Event) Stamp() string = s"${e.Name} at ${e.clock.Now()} ${e.clock.Zone()}"
func main() {
val fixed = Clock(Now = () => 12, Zone = () => "UTC")
Println(Event("deploy", fixed).Stamp()) // deploy at 12 UTC
}
See also: Language Reference - Interface Literals and the Dependency Injection guide
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)
}
Immutable[T]A shorthand field declared Immutable[T] is the same field as one declared T: it takes a T however it is supplied, and reads as a T.
package main
struct Counter(Label string, Hits Immutable[int64] = 1)
func main() {
val named = Counter(Hits = 3, Label = "named")
val copied = named.Copy(Hits = 4)
Println(s"${named.Hits} ${copied.Hits} ${Counter("d").Hits}") // 3 4 1
}
Ptr and OptionPtr&x is a Ptr[T], a pointer that is never nil; a pointer that may be absent is an OptionPtr[T]; a Go *T becomes one or the other once, at the boundary.
package main
struct Node(Value int, Next OptionPtr[Node])
func (n Ptr[Node]) Sum() int = n.Value + n.Next.Map((next) => next.Sum()).GetOrElse(0)
// A Go-style lookup: nil when nothing is found.
func find(found bool, n *Node) *Node = if (found) n else nil
func main() {
val tail = &Node{Value: 3}
val head = &Node(1, SomePtr(&Node(2, SomePtr(tail))))
Println(head.Sum()) // 6
Println(RequirePtr(find(true, head)).Value) // 1
Println(OptionPtrOf(find(false, head)).IsEmpty()) // true
}
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 infers the type arguments of generic function calls from their arguments. 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() {
// getImm(42) is an Immutable[int], which GALA then automatically unwraps to int
var x = identity(getImm(42))
Println(s"x: $x")
}
Writing only the leading type arguments of a generic constructor binds those and infers the rest:
package main
struct Pair[A any, B any](First A, Second B)
func main() {
val p = Pair[int64](1, "one") // Pair[int64, string]
val q = Pair[float64](Second = true, First = 2)
Println(s"${p.First} ${p.Second} ${q.First} ${q.Second}")
}
A generic struct construction takes the type arguments its fields leave open from the type it is expected to have:
package main
struct Tag[T any](Name string) // T is a phantom: no field names it
func label() Tag[int] = Tag("count")
func main() {
val t Tag[string] = Tag("name")
Println(s"${label().Name} ${t.Name}")
}
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
}
A tuple arm made only of wildcards and bindings (or nested such tuples) matches every value, so it closes a match too:
func divmod(a int, b int) Tuple3[int, int, string] =
if (b == 0) (0, 0, "division by zero") else (a / b, a % b, "ok")
val result = divmod(17, 0) match {
case (q, r, "ok") => s"$q remainder $r"
case (_, _, err) => s"Error: $err" // covers every other Tuple3
}
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')
// A val of function type types the lambdas passed to it
val apply = (h func(string) string) => h("gala")
val shouted = apply((w) => w + "!")
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"
}
Exists is true when the Option holds a value that satisfies the predicate;
ForAll is true when it holds no value or one that does.
package main
func main() {
val age = Some(34)
val unknown = None[int]()
Println(age.Exists((a) => a >= 18)) // true
Println(unknown.Exists((a) => a >= 18)) // false — None never satisfies Exists
Println(unknown.ForAll((a) => a >= 18)) // true — None always satisfies ForAll
}
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)
Try(os.MkdirAll("/tmp/test", 0755))
dir.OnSuccess((d) => Println(s"Temp dir: $d"))
}
A Go call returning (T, error) is a Try[T]; (A, B, error) is a Try of a
Tuple; (A, B, C) is a Tuple3. Match on them directly — no Try(...) needed:
package main
import (
"net"
"strconv"
"strings"
)
func main() {
// (int, error) -> Try[int]
val intResult = strconv.Atoi("42") match {
case Success(n) => s"Parsed: $n"
case Failure(err) => s"Error: ${err.Error()}"
}
Println(intResult) // Parsed: 42
// (string, string, error) -> Try[Tuple[string, string]]
val hostPort = net.SplitHostPort("localhost:8080") match {
case Success((host, port)) => s"host=$host port=$port"
case Failure(err) => s"Error: ${err.Error()}"
}
Println(hostPort) // host=localhost port=8080
// (string, string, bool) -> Tuple3[string, string, bool]
val (key, value, found) = strings.Cut("mode=fast", "=")
Println(s"$key -> $value ($found)") // mode -> fast (true)
}
See also: Language Reference - Go functions that return several results
A function or method that Go calls with several results declares Go’s result
list; its body gives their one GALA value, a Try for (T, error) or a Tuple
for (A, B). A lambda passed where Go expects such a function works the same
way:
package main
import (
"encoding/json"
"fmt"
"strconv"
"sync"
. "martianoff/gala/go_interop"
)
struct LineCounter(var Lines int)
// io.Writer
func (c *LineCounter) Write(p []byte) (int, error) {
c.Lines = c.Lines + 1
Success(p.Size())
}
struct Celsius(Degrees int)
// json.Marshaler
func (c Celsius) MarshalJSON() ([]byte, error) = Success(ToBytes(s"\"${c.Degrees}C\""))
func divmod(a int, b int) (int, int) = (a / b, a % b)
func main() {
var counter = LineCounter(0)
fmt.Fprintln(&counter, "one")
fmt.Fprintln(&counter, "two")
Println(counter.Lines) // 2
Println(json.Marshal(Celsius(21)).Map((b) => b.Size())) // Success(5)
val (q, r) = divmod(17, 5) // a GALA call of it is a Tuple
Println(s"$q $r") // 3 2
// A lambda returning Go's (int, error)
val port = sync.OnceValues(() => strconv.Atoi("8080"))
Println(port()) // Success(8080)
}
Output:
2
Success(5)
3 2
Success(8080)
See also: Language Reference - Returning several results to Go
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. Sorted, SortWith and SortBy are stable: elements that compare equal keep their original relative order.
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)
// Sorting is stable: equal keys keep their input order
val words = ArrayOf("bb", "a", "cc", "d")
Println(words.SortBy((w) => w.Size())) // Array(a, d, bb, cc)
// 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
An alias can take type parameters; a lambda in its place is typed by the instantiation:
package main
type Conv[A any, B any] func(A) B
struct Step[A any, B any](In A, Run Conv[A, B])
func main() {
val step = Step(In = 3, Run = (x) => s"<$x>")
Println(step.Run(step.In))
}
Output:
<3>
A generic alias can fix some of its target’s arguments. Result[int] is
Either[AppError, int], so Left and Right take their type arguments from
it and a match on it sees Either’s variants:
package main
import . "martianoff/gala/collection_immutable"
struct AppError(Code int)
type Result[T any] Either[AppError, T]
type StrMap[V any] HashMap[string, V]
func parse(s string) Result[int] = if (s == "") Left(AppError(1)) else Right(s.Size())
func describe(r Result[int]) string = r match {
case Right(n) => s"ok $n"
case Left(e) => s"error ${e.Code}"
}
func main() {
Println(describe(parse("four")))
Println(describe(parse("")))
val counts StrMap[int] = HashMapOf(("a", 1))
Println(counts.Put("b", 2).Size())
}
Output:
ok 4
error 1
2
An opaque type is a distinct type over a scalar: typed IDs and units that
cannot be mixed up, with methods, operators, HashMap keys and sorting, and
the bare value on the wire:
package main
import (
. "martianoff/gala/collection_immutable"
"martianoff/gala/json"
)
opaque type UserID int64
opaque type Millis int64
func (m Millis) Seconds() float64 = float64(m) / 1000.0
struct Visit(User UserID, Took Millis)
func main() {
val id = UserID(7)
val took = Millis(1200) + 300
val names = EmptyHashMap[UserID, string]().Put(id, "ada")
Println(names.Get(id), took.Seconds(), took > Millis(1000))
Println(ArrayOf(UserID(3), UserID(1)).Sorted())
Println(json.Codec[Visit](json.SnakeCase()).Encode(Visit(id, took)).Get())
}
Output:
Some(ada) 1.5 true
Array(1, 3)
{"user":7,"took":1500}
case UserID(n) unwraps an opaque value (n is the int64); on an any
subject it first checks the value is a UserID, which a plain int64 is not.
A phantom type parameter gives each entity its own ID type:
package main
struct Order(Total int)
opaque type UserID int64
opaque type Id[T any] int64
func describe(id UserID) string = id match {
case UserID(0) => "nobody"
case UserID(n) => s"user $n"
case _ => "unreachable"
}
func kind(v any) string = v match {
case UserID(n) => s"a UserID $n"
case n: int64 => s"an int64 $n"
case _ => "other"
}
func orderTotal(id Id[Order]) int = int(id) * 10
func main() {
Println(describe(UserID(0)), describe(UserID(7)))
Println(kind(UserID(5)), kind(int64(5)))
Println(orderTotal(Id[Order](3)))
}
Output:
nobody user 7
a UserID 5 an int64 5
30
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 |