return 类型密封时的满射性检查
Surjectivity check when return type is sealed
Scala 可以在密封类型上的模式匹配不详尽时发出警告,但是我们可以检查函数 return 在 return 类型密封时的所有情况吗?例如,考虑以下 ADT
sealed trait Foo
case object Bar extends Foo
case object Qux extends Foo
然后函数 f: Foo => String
代数数据类型 Foo
def f(x: Foo): String = x match {
case Bar => "bar"
}
发出警告
match may not be exhaustive.
It would fail on the following input: Qux
def f(x: Foo) = x match {
当 return 类型是 ADT 时是否可能引发类似的非耗尽警告,例如在 f: String => Foo
的以下实现中:
def f(x: String): Foo = x match {
case "bar" => Bar
// warn because we never return Qux
}
也许这不是一个真正的答案,但对于评论来说太长了。
模式匹配和函数return值是两个不同的东西。前者在 type 级别上运行,后者在 value 级别上运行。当你在 Bar
上进行模式匹配时,你就是在类型上进行模式匹配(就像 Int
一样)。但是当你 return Bar
时,你 returning 了 case 对象值(就像 42
)。
满射函数定义为:
For every member y of the codomain, there exists at least one member
x of the domain, such that f(x) = y.
现在很容易看出为什么这个检查不可行/不可能。如果您的 Bar
不是案例对象,而是 class 怎么办?例如
final case class Bar(name: String, surname: String, age: Int)
您需要预料到 Bar
的所有可能值都会被使用(例如姓名 = "John"、姓氏 = "Smith"、年龄 = 42)。
当然,这不是你想要的;你描述的是每个子类型只有一个居民的场景,因为 Bar
和 Qux
基本上是枚举,我明白为什么这样的检查对你有意义。但是它必须针对每个(子)类型的任意数量居民的一般情况来实现 - 它需要验证 codomain 至少包含一个 Bar
类型的值,至少一个 [=] 类型的值18=] 等听起来不是很有用。
正如我所说,这并不是真正的答案,但我想让您深入了解您要问的到底是什么。 :) 也许有人写了一些带有反射 and/or 宏的东西,可以提供这样的检查,但据我所知不是。希望有了 Scala 3 枚举,您将永远不需要编写这样的函数。
以下是@LuisMiguelMejíaSuárez 和@slouc 建议的枚举示例,它们确实提供了案例穷举:
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")
enum Foo {
case Bar
case Qux
}
Foo.enumValueNamed("Qux"))
即使使用参数化密封类型,这两种方法也能正常工作。
Scala 可以在密封类型上的模式匹配不详尽时发出警告,但是我们可以检查函数 return 在 return 类型密封时的所有情况吗?例如,考虑以下 ADT
sealed trait Foo
case object Bar extends Foo
case object Qux extends Foo
然后函数 f: Foo => String
代数数据类型 Foo
def f(x: Foo): String = x match {
case Bar => "bar"
}
发出警告
match may not be exhaustive.
It would fail on the following input: Qux
def f(x: Foo) = x match {
当 return 类型是 ADT 时是否可能引发类似的非耗尽警告,例如在 f: String => Foo
的以下实现中:
def f(x: String): Foo = x match {
case "bar" => Bar
// warn because we never return Qux
}
也许这不是一个真正的答案,但对于评论来说太长了。
模式匹配和函数return值是两个不同的东西。前者在 type 级别上运行,后者在 value 级别上运行。当你在 Bar
上进行模式匹配时,你就是在类型上进行模式匹配(就像 Int
一样)。但是当你 return Bar
时,你 returning 了 case 对象值(就像 42
)。
满射函数定义为:
For every member y of the codomain, there exists at least one member x of the domain, such that f(x) = y.
现在很容易看出为什么这个检查不可行/不可能。如果您的 Bar
不是案例对象,而是 class 怎么办?例如
final case class Bar(name: String, surname: String, age: Int)
您需要预料到 Bar
的所有可能值都会被使用(例如姓名 = "John"、姓氏 = "Smith"、年龄 = 42)。
当然,这不是你想要的;你描述的是每个子类型只有一个居民的场景,因为 Bar
和 Qux
基本上是枚举,我明白为什么这样的检查对你有意义。但是它必须针对每个(子)类型的任意数量居民的一般情况来实现 - 它需要验证 codomain 至少包含一个 Bar
类型的值,至少一个 [=] 类型的值18=] 等听起来不是很有用。
正如我所说,这并不是真正的答案,但我想让您深入了解您要问的到底是什么。 :) 也许有人写了一些带有反射 and/or 宏的东西,可以提供这样的检查,但据我所知不是。希望有了 Scala 3 枚举,您将永远不需要编写这样的函数。
以下是@LuisMiguelMejíaSuárez 和@slouc 建议的枚举示例,它们确实提供了案例穷举:
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")
enum Foo {
case Bar
case Qux
}
Foo.enumValueNamed("Qux"))
即使使用参数化密封类型,这两种方法也能正常工作。