如何根据 Scala 中的另一个参数限制泛型参数的参数类型?
How do I limit the parameter types of a generic argument based on another parameter in Scala?
我正在尝试创建不同配置的映射,其中每个配置都有一个给定的关键对象和一些选项,例如
FirstConfig
可以是:
FirstConfigOptionA
FirstConfigOptionB
SecondConfig
可以是:
SecondConfigOptionA
SecondConfigOptionB
- ...
而且我在 setter 函数的一般输入和签名方面遇到了问题,因此它会在编译时检查我是否提供了正确的对象,例如
// 1. this should compile normally
set(FirstConfig, FirstConfigOptionA)
// 2. should NOT compile due to `SecondConfigOptionA` parameter not being a valid option for `FirstConfig`
set(FirstConfig, SecondConfigOptionA)
到目前为止,我的尝试仍然允许上面的第二种情况编译。
abstract sealed class Configuration
trait OptionKey[T <: Configuration] {}
trait OptionVariant[T <: Configuration] {}
// First Config
trait FirstConfig extends Configuration
object FirstConfigKey extends OptionKey[FirstConfig];
object FirstConfigOptionA extends OptionVariant[FirstConfig]
object FirstConfigOptionB extends OptionVariant[FirstConfig]
// Second Config
trait SecondConfig extends Configuration
object SecondConfigKey extends OptionKey[SecondConfig];
object SecondConfigOptionA extends OptionVariant[SecondConfig]
object SecondConfigOptionB extends OptionVariant[SecondConfig]
def set[T](k: OptionKey[T], v: OptionVariant[T]): Unit = {}
set(FirstConfigKey, FirstConfigOptionA)
set(FirstConfigKey, SecondConfigOptionA) // This still compiles
我也试过使用枚举得到类似的结果:
object FirstConfig extends Enumeration {
type FirstConfig = Value
val FirstConfigOptionA, FirstConfigOptionB = Value
}
object SecondConfig extends Enumeration {
type SecondConfig = Value
val SecondConfigOptionA, SecondConfigOptionB = Value
}
def set(k: Enumeration, v: Enumeration#Value): Unit = {}
set(FirstConfig, FirstConfig.FirstConfigOptionA)
set(FirstConfig, SecondConfig.SecondConfigOptionA) // This still compiles
表达配置与其可用选项之间这种关系的正确方法是什么,或者 set
的签名应该是什么来执行它?
使用 path-dependant 这样的类型怎么样:
trait Configuration {
sealed trait OptionKey
val key: OptionKey
sealed trait OptionVariant
}
object Configuration {
def set(config: Configuration)(variant: config.OptionVariant): Unit = {
println(s"${config} - ${config.key} - ${variant}")
}
}
case object FirstConfig extends Configuration {
private case object FirstConfigKey extends OptionKey
override final val key: OptionKey = FirstConfigKey
case object FirstConfigOptionA extends OptionVariant
case object FirstConfigOptionB extends OptionVariant
}
case object SecondConfig extends Configuration {
private case object SecondConfigKey extends OptionKey
override final val key: OptionKey = SecondConfigKey
case object SecondConfigOptionA extends OptionVariant
case object SecondConfigOptionB extends OptionVariant
}
您可以看到它按预期工作here。
为什么需要将它们存储为 key/value 对?您可以将其表示为代数数据类型:
enum FirstConfig:
case OptionA
case OptionB
enum SecondConfig:
case OptionA
case OptionB
enum Config:
case First(value: FirstConfig)
case Second(value: SecondConfig)
def set(config: Config): Unit = …
我正在尝试创建不同配置的映射,其中每个配置都有一个给定的关键对象和一些选项,例如
FirstConfig
可以是:FirstConfigOptionA
FirstConfigOptionB
SecondConfig
可以是:SecondConfigOptionA
SecondConfigOptionB
- ...
而且我在 setter 函数的一般输入和签名方面遇到了问题,因此它会在编译时检查我是否提供了正确的对象,例如
// 1. this should compile normally
set(FirstConfig, FirstConfigOptionA)
// 2. should NOT compile due to `SecondConfigOptionA` parameter not being a valid option for `FirstConfig`
set(FirstConfig, SecondConfigOptionA)
到目前为止,我的尝试仍然允许上面的第二种情况编译。
abstract sealed class Configuration
trait OptionKey[T <: Configuration] {}
trait OptionVariant[T <: Configuration] {}
// First Config
trait FirstConfig extends Configuration
object FirstConfigKey extends OptionKey[FirstConfig];
object FirstConfigOptionA extends OptionVariant[FirstConfig]
object FirstConfigOptionB extends OptionVariant[FirstConfig]
// Second Config
trait SecondConfig extends Configuration
object SecondConfigKey extends OptionKey[SecondConfig];
object SecondConfigOptionA extends OptionVariant[SecondConfig]
object SecondConfigOptionB extends OptionVariant[SecondConfig]
def set[T](k: OptionKey[T], v: OptionVariant[T]): Unit = {}
set(FirstConfigKey, FirstConfigOptionA)
set(FirstConfigKey, SecondConfigOptionA) // This still compiles
我也试过使用枚举得到类似的结果:
object FirstConfig extends Enumeration {
type FirstConfig = Value
val FirstConfigOptionA, FirstConfigOptionB = Value
}
object SecondConfig extends Enumeration {
type SecondConfig = Value
val SecondConfigOptionA, SecondConfigOptionB = Value
}
def set(k: Enumeration, v: Enumeration#Value): Unit = {}
set(FirstConfig, FirstConfig.FirstConfigOptionA)
set(FirstConfig, SecondConfig.SecondConfigOptionA) // This still compiles
表达配置与其可用选项之间这种关系的正确方法是什么,或者 set
的签名应该是什么来执行它?
使用 path-dependant 这样的类型怎么样:
trait Configuration {
sealed trait OptionKey
val key: OptionKey
sealed trait OptionVariant
}
object Configuration {
def set(config: Configuration)(variant: config.OptionVariant): Unit = {
println(s"${config} - ${config.key} - ${variant}")
}
}
case object FirstConfig extends Configuration {
private case object FirstConfigKey extends OptionKey
override final val key: OptionKey = FirstConfigKey
case object FirstConfigOptionA extends OptionVariant
case object FirstConfigOptionB extends OptionVariant
}
case object SecondConfig extends Configuration {
private case object SecondConfigKey extends OptionKey
override final val key: OptionKey = SecondConfigKey
case object SecondConfigOptionA extends OptionVariant
case object SecondConfigOptionB extends OptionVariant
}
您可以看到它按预期工作here。
为什么需要将它们存储为 key/value 对?您可以将其表示为代数数据类型:
enum FirstConfig:
case OptionA
case OptionB
enum SecondConfig:
case OptionA
case OptionB
enum Config:
case First(value: FirstConfig)
case Second(value: SecondConfig)
def set(config: Config): Unit = …