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.