Scala can warn when pattern match on a sealed type is not exhaustive, however can we check that a function returns all cases when the return type is sealed? For example, consider the following ADT
sealed trait Foo
case object Bar extends Foo
case object Qux extends Foo
Then function f: Foo => String
on the algebraic data type Foo
def f(x: Foo): String = x match {
case Bar => "bar"
}
raises warning
match may not be exhaustive.
It would fail on the following input: Qux
def f(x: Foo) = x match {
Is it possible to raise a similar non-exhaustion warning when return type is an ADT such as in the following implementation of f: String => Foo
:
def f(x: String): Foo = x match {
case "bar" => Bar
// warn because we never return Qux
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With