【问题标题】:How do I limit the parameter types of a generic argument based on another parameter in Scala?如何根据Scala中的另一个参数限制泛型参数的参数类型?
【发布时间】:2022-02-05 11:33:48
【问题描述】:

我正在尝试创建一个不同配置的映射,其中每个配置都有一个给定的键对象和一些选项,例如

  • 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 的签名来强制执行它?

【问题讨论】:

  • 你能在第一种情况下尝试这样的选项吗? def set[T &lt;: Configuration](k: OptionKey[T], v: OptionVariant[T]): Unit = {}
  • 不,这对我不起作用。无论如何,谢谢。

标签: scala generics types enumeration


【解决方案1】:

如何使用像这样的路径依赖类型:

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

【讨论】:

  • 谢谢!这就是我要找的
【解决方案2】:

为什么需要将它们存储为键/值对?您可以将其表示为代数数据类型:

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 = …

【讨论】:

  • 请不要在回答没有scala-3标签的问题时使用Scala 3语法。
  • 老兄,我不再浪费时间用 Scala 2 语法编写 ADT。
  • 但是很高兴在别人尝试这个但它不起作用时浪费了他们的时间?不错。
  • 我们必须同意不同意这一点。
  • fwiw,我正在使用 Scala 2。无论如何,感谢您的回答,作为 Scala 的整体新手,很高兴知道它在较新的版本中可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 1970-01-01
  • 2021-10-31
相关资源
最近更新 更多