When it fires. A type pattern case x: I tests for an interface I, and
some type has every method of I, but at least one of them is generated as a
free function rather than a Go method. A method of a generic type whose
signature names another generic type, such as HashMap.Keys() or
Array.ToList(), is generated that way unless an interface that the type’s
own package can see needs it (see
Methods of generic types that Go sees as functions).
Go’s type test never matches a value of that type, so the arm would quietly
never be taken.
Minimal repro.
package main
import . "martianoff/gala/collection_immutable"
type HasKeys interface {
Keys() HashSet[string]
}
func probe(x any) string = x match {
case h: HasKeys => "keys"
case _ => "none"
}
func main() = Println(probe(HashMapOf(("a", 1))))
Error output.
error[GALA-E0109]: a value of HashMap never matches `HasKeys`: its Keys is generated as a free function (it names another generic type), so the type does not have it in Go
--> main.gala:10:10
|
10 | case h: HasKeys => "keys"
| ^^^^^^^^^^ match the concrete type
|
= hint: match the concrete type (`case x: HashMap`) and call Keys on it, or declare HasKeys where HashMap's package sees it so the method stays a method
Fix. Match the concrete type and call the method on it:
package main
import . "martianoff/gala/collection_immutable"
func probe(x any) string = x match {
case m: HashMap[string, int] => s"keys ${m.Keys()}"
case _ => "none"
}
func main() = Println(probe(HashMapOf(("a", 1))))
For a type of your own, declare the interface in the type’s package, or in a package that package imports. The method then stays a method, and both the type pattern and the static use work.
Rationale. Go compiles every method of a generic type once per type
argument, whether or not anything calls it. A method that names another
generic type would make every instantiation compile that type’s methods too,
so it becomes a free function that is compiled only where it is called. A
call m.Keys() works the same either way. Only Go’s method set changes, and
with it which interfaces the type implements. Using the value as such an
interface statically is GALA-E0100.