When it fires. A variant of a sealed type is named where a type is
expected. A case of a sealed type declares a constructor and an extractor,
not a type of its own: every value Circle(...) builds is a Shape. The
check covers every type position — a parameter, a result, a struct or variant
field, a val / var annotation, a type argument (in a type or in a call
such as ArrayOf[Circle](...) or an extractor pattern such as
case Some[Circle](c)), a typed pattern (case c: Circle), a
receiver and an alias target — for a variant of a sealed type declared in the
same package, in an imported package (shapes.Circle), or in std
(Some[int], Left[string, int]).
Minimal repro.
package main
sealed type Shape {
case Circle(R float64)
case Square(S float64)
}
func radius(c Circle) float64 = c.R
func main() {
Println(radius(Circle(2.0)))
}
Error output.
error[GALA-E0061]: Circle is a variant of sealed type Shape, not a type
--> main.gala:8:15
|
8 | func radius(c Circle) float64 = c.R
| ^^^^^^ use the sealed type Shape here
|
= hint: use the sealed type Shape here; Circle(...) builds one, and `case Circle(...)` in a match reaches the variant's fields
Fix. Take the sealed type, and match on the variant to reach its fields:
package main
sealed type Shape {
case Circle(R float64)
case Square(S float64)
}
func radius(s Shape) float64 = s match {
case Circle(r) => r
case Square(_) => 0.0
}
func main() {
Println(radius(Circle(2.0)))
}
When a function only makes sense for one variant, say so in its result
instead: return an Option and let the caller decide what the other variants
mean.
package main
sealed type Shape {
case Circle(R float64)
case Square(S float64)
}
func radius(s Shape) Option[float64] = s match {
case Circle(r) => Some(r)
case _ => None()
}
func main() {
Println(radius(Circle(2.0)).GetOrElse(0.0))
}
A std variant. Some, None, Left, Right, Success and Failure
are variants of Option, Either and Try, so the same rule applies to
them:
package main
import . "martianoff/gala/collection_immutable"
func main() {
val found Array[Some[int]] = ArrayOf(Some(1), Some(2))
Println(found)
}
error[GALA-E0061]: Some[int] is a variant of sealed type Option[int], not a type
--> main.gala:6:21
|
6 | val found Array[Some[int]] = ArrayOf(Some(1), Some(2))
| ^^^^ use the sealed type Option[int] here
|
= hint: use the sealed type Option[int] here; Some(...) builds one, and `case Some(...)` in a match reaches the variant's fields
Write Array[Option[int]].
Rationale. In the generated Go, a sealed type is one struct that holds the
fields of every variant, and each variant is an empty companion struct that
carries its Apply and Unapply. A variant named as a type compiled to that
empty struct, so the program failed in go build — c.R undefined (type
Circle has no field or method R) and cannot use Circle{}.Apply(2.0) (value
of type Shape) as Circle value — and a typed pattern case c: Circle asked
whether a Shape is the companion struct, which is never true, so the arm
silently never matched. GALA has no type for a single variant: narrowing to
one is what a match arm does.
What still works. The sealed type in any type position, a variant as a
constructor (Circle(2.0)) and as a pattern (case Circle(r)), and a type or
type parameter you declare under a variant’s name, which shadows it.