return inside a value-producing matchWhat it means. A match expression is used as a value — passed as an argument, used as an operand, or returned as an expression — and one of its branches ends with a bare return (a return with no value). A match a local val or var is initialized with, or a variable is assigned, is not such a value: it is lowered as statements, and a bare return in it leaves the enclosing function. That is fine when the function returns nothing; when it returns a value, a bare return there is GALA-E0015 too — give the return a value.
package main
import "os"
func run(path string) {
Println(Try(os.ReadFile(path)) match {
case Success(b) => string(b)
case Failure(err) => {
Println(s"error: ${err.Error()}")
return // bare return in a value-producing branch
}
})
}
error[GALA-E0015]: bare `return` inside a match branch whose result is used as a value
--> main.gala:6:13
|
6 | Println(Try(os.ReadFile(path)) match {
| ^^^ the match is wrapped in a function that must return string
|
= hint: the match is wrapped in a function that must return string; in a function that returns nothing, initialize a `val` with the match first (a `return` in it then leaves the function), restructure to early-exit before the match, or use combinators like .Recover / .GetOrElse. See docs/errors/GALA-E0015.md
The caret marks the match being used as a value, and the hint names the result type its wrapper must produce.
A match-as-value lowers to an immediately-invoked function expression:
fmt.Println(func() string {
switch ... {
case Success: return string(b)
case Failure: fmt.Println(...); return // invalid: must return string
}
}(...))
A bare return there does not exit the enclosing function — it exits the wrapper. Because the wrapper has a non-void return type, Go rejects it with “not enough return values”.
1. Initialize a val with the match. The match is then lowered as statements, and the bare return leaves run:
func run(path string) {
val data = Try(os.ReadFile(path)) match {
case Success(b) => string(b)
case Failure(err) => {
Println(s"error: ${err.Error()}")
return
}
}
Println(data)
}
2. Early-exit before the match. Handle the failure first, then destructure the success case:
func run(path string) {
val result = Try(os.ReadFile(path))
if (result.IsFailure()) {
Println(s"error: ${result.GetError().Error()}")
return
}
Println(string(result.Get()))
}
3. Use combinators. Map, Recover, and GetOrElse avoid the wrapper entirely:
func run(path string) {
val data = Try(os.ReadFile(path))
.Map((b) => string(b))
.GetOrElse("")
if (data == "") { return }
Println(data)
}
4. Make the match the function body. When the match is the function’s result, each branch legitimately returns a value.
GALA’s match-as-value form has expression semantics: every branch must contribute a value of the match’s result type. A bare return is a statement-level control-flow operation whose scope — outer function or enclosing wrapper — is ambiguous where the match is lowered to a wrapper; a match a val is initialized with is lowered as statements instead. Rather than silently pick one reading and generate surprising Go, the transpiler rejects the construct and points at the explicit rewrites.
Try, Option, and Either combinators