GALA-E0023 — Undefined variable

What it means. A name has no binding in the current environment. Three usual causes:


Code that triggers it

package main

func main() {
    Println(x + 1)
}

Compiler message

error[GALA-E0023]: undefined: x
  --> e0023.gala:4:13
  |
4 |     Println(x + 1)
  |             ^ check the spelling, add the import that introduces this name…
  |
  = hint: check the spelling, add the import that introduces this name, or declare it — every identifier must resolve to a binding, a declaration in this package, or an imported symbol

The diagnostic is framed and carries the offending identifier’s own span, so the caret lands on the name itself.


How to fix it

Fix the typo, add the import that introduces the name, or move the reference inside the scope where it is bound. Pattern bindings only exist inside their own arm:

val label = shape match {
    case Circle(r) => s"circle r=$r"     // r is bound here
    case Square(s) => s"square s=$s"     // ...and s only here
}

If the name is meant to come from a dot-imported package, confirm the import is present in this file — sibling files’ imports do not propagate.

A bare GALA name must come from this file’s package, a package this file dot-imports, or the std prelude. A package that is loaded only because one of your imports uses it does not count. The GALA strings package loads the collection packages, but a file that imports only strings still cannot call ArrayOf or write Array[int] unqualified. A package imported by name is reached through its qualifier:

import "martianoff/gala/collection_immutable"

val xs = ArrayOf(1, 2)                       // GALA-E0023
val ys = collection_immutable.ArrayOf(1, 2)  // ok

Type names are checked the same way. This covers a parameter, result, struct field, type argument, val annotation, alias target or constraint. The name must be a predeclared Go type, a type parameter, or a type declared by this package, a dot-imported package or the std prelude. A Go package imported by name is reached through its qualifier here too, so time.Duration works and a bare Duration does not. Without the import, func total(xs Array[int]) is reported at Array, and the hint names the packages that declare it.

A .gen.go that another generator wrote (oapi-codegen, stringer, a protoc plugin) counts as Go source of its package, here and in the build. It works the same in the package that holds it and through a dot or qualified import. A .gen.go the GALA transpiler left in the tree is not counted, since it restates the .gala sources. It is told apart by its first line, // Code generated by GALA transpiler. DO NOT EDIT. A generated file must not have the name a .gala file beside it is transpiled to (server.gen.go beside server.gala); the build reports that.


Why the rule exists

Undefined names are the most common failure during early development, and the alternative was letting them fall through to the Go compiler, which reports them against generated code. Resolving every identifier at the GALA level keeps the error on the line you wrote, and the stable code lets editors and CI tools attach extra context.

Scope. Identifier resolution, in value and type position. A signature type that the analyzer resolved to a package this file did not import gets the more specific GALA-E0025. Type mismatches between names that both resolve are left to the Go compiler.