Surjectivity check when return type is sealed
Maybe this is not really an answer, but it's too long for a comment anyway.
Pattern matching and function return values are two different things. Former operates on the type level, latter on the value level. When you pattern match on Bar
, you're pattern matching on the type (just like e.g. Int
). But when you return Bar
, you're returning the case object value (just like e.g. 42
).
Surjective function is defined as:
For every member y of the codomain, there exists at least one member x of the domain, such that f(x) = y.
Now it's easy to see why this check is not feasible / possible. What if your Bar
wasn't a case object, but a class? E.g.
final case class Bar(name: String, surname: String, age: Int)
You would need to expect every possible value of Bar
to be used (e.g. name = "John", surname = "Smith", age = 42).
Of course, this is not what you intended; what you described is a scenario where each subtype has exactly one inhabitant, because Bar
and Qux
are basically enums, and I can see why such a check might make sense to you. But it would have to be implemented for the general case of arbitrary number of inhabitants per (sub)type - it would need to verify that codomain contains at least one value of type Bar
, at least one value of type Qux
etc. which doesn't sound very useful.
As I said, this is not really an answer, but I wanted to give you an insight into what exactly it is that you're asking. :) Perhaps someone wrote something with reflection and/or macros which could provide such a check, but not to my knowledge. Hopefully with Scala 3 enums you will never need to write such a function anyway.
Here are enumeration examples suggested by @LuisMiguelMejíaSuárez and @slouc which do provide case exhaustion:
enumeratum
import enumeratum._
sealed trait Foo extends EnumEntry
object Foo extends Enum[Foo] {
val values = findValues
case object Bar extends Foo
case object Qux extends Foo
}
Foo.withName("Qux")
Scala 3 Enumerations
enum Foo {
case Bar
case Qux
}
Foo.enumValueNamed("Qux"))
Both methods worked fine even with parameterised sealed type.