简体   繁体   English

如何在 Scala 的编译时验证类型存在

[英]How can I verify type existence on compile time in Scala

I have the following traits and classes:我具有以下特征和类别:

sealed trait Signal

sealed trait Description[T]

final case class S1(name: String) extends Signal

final case class D1(name: String) extends Description[S1]

What I try to achieve is that anyone who wants to add Signal will have (at compile time) to create a description.我试图实现的是,任何想要添加 Signal 的人都可以(在编译时)创建描述。

I don't want to change the signature of Description but for sure not of Signal我不想更改Description的签名,但肯定不是Signal

I set my compiler to fail on warning, so I can leverage the fact that my ADT is sealed.我将编译器设置为在警告时失败,因此我可以利用我的 ADT 是密封的这一事实。

My idea was to have such a "compilation guard":我的想法是有这样一个“编译后卫”:

def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }

but I get the following error:但我收到以下错误:

<console>:17: error: type mismatch;
 found   : D1
 required: Description[S]
       def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }
                                                                                              ^
def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }

can't compile for the same reason as无法编译的原因与

def returnItself[S <: Signal](s: S): S = s match { case S1(name) => S1(name) }

Reasons are explained here in details:原因在这里详细解释:

Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter? 如果 A 的泛型子类型被声明为返回参数,为什么我不能返回 A 的具体子类型?

Type mismatch on abstract type used in pattern matching 模式匹配中使用的抽象类型的类型不匹配

If you don't want to mix Description logic to ADT or define instances of a type class like SignalMapper manually you can use Shapeless如果您不想将Description逻辑混合到 ADT 或手动定义 class 类型的实例,例如SignalMapper ,您可以使用 Shapeless

import shapeless.ops.coproduct.Mapper
import shapeless.{:+:, CNil, Coproduct, Generic, Poly1}

def compilationGuard[C <: Coproduct]()(implicit
  gen: Generic.Aux[Signal, C],
  mapper: Mapper[uniqueDescriptionPoly.type, C]
) = null

object uniqueDescriptionPoly extends Poly1 {
  implicit def cse[S <: Signal, C1 <: Coproduct](implicit
    gen1: Generic.Aux[Description[S], C1],
    ev: C1 <:< (_ :+: CNil)
  ): Case.Aux[S, Null] = null
}

compilationGuard()

Testing:测试:

final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1] 
// doesn't compile

final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
final case class D2(name: String) extends Description[S1]
// doesn't compile

final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
final case class D2(name: String) extends Description[S2]
// compiles

What I try to achieve is that anyone who wants to add Signal will have (at compile time to create a description.我试图实现的是任何想要添加 Signal 的人都将拥有(在编译时创建描述。

The easy way to do so is to make it part of Signal :这样做的简单方法是使其成为Signal的一部分:

sealed trait Signal[S <: Signal[S, D], D <: Description[S]] {
  // optionally
  def description: D
}

final case class S1(name: String) extends Signal[S1, D1] {
  def description = D1(name)
}

or或者

sealed trait Signal[S <: Signal[S]] {
  type Descr <: Description[S]
  // optionally
  def description: Descr
}

final case class S1(name: String) extends Signal[S1] {
  type Descr = D1
  def description = D1(name)
}

Of course it isn't far from the simpler当然,它离更简单不远了

sealed trait Signal[S <: Signal] {
  def description: Description[S]
}

depending on your requirements.根据您的要求。

Your program is failing, because it can't prove, that type S is S1 .您的程序失败了,因为它无法证明类型SS1

Instead of doing pattern matching, you could introduce typeclass which would do a mapping on compile time:您可以引入类型类,而不是进行模式匹配,它会在编译时进行映射:

trait SignalMapper[S] { //typeclass handling of mapping S to D
  type D <: Description[S]

  def map(signal: S): D
}

//instance of typeclass SignalMapper for S1
//if you'd put it in a companion object of S1, it would be always in scope
object S1 { 

  implicit val mapperS1: SignalMapper[S1] = new SignalMapper[S1] {
    type D = D1

    def map(signal: S1) = D1(signal.name)
  }

}

Then you can rewrite compilationGuard as:然后您可以将compileGuard重写为:

def compilationGuard[S <: Signal](s: S)(implicit mapper: SignalMapper[S]): Description[S] = mapper.map(s)

Scastie斯卡斯蒂

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Scala中,如何对伴随对象执行编译时类型检查? - In Scala, How to perform compile-time type check on companion object? 如何在Scala中强制编译时间函数的参数为​​特定类型? - How to force a compile time function's argument to be of a specific type in Scala? 如何对Scala方法的值强制执行编译时约束? - How can I enforce compile-time constraints on values for Scala methods? 如何提供编译时保证我的方法将返回它在Scala中获取的相同对象? - How can I provide a compile-time guarantee that my method will return the same object it gets in Scala? Scala:在编译时验证 class 参数不是 instanceOf 特征 - Scala: verify class parameter is not instanceOf a trait at compile time 类型类的scala存在类型 - scala existence type of type class Scala 编译时错误,缺少参数类型 - Scala compile time error, missing parameter type Scala:如何在编译时不知道类型的情况下使用类型参数和清单调用方法? - Scala: How to invoke method with type parameter and manifest without knowing the type at compile time? sbt-buildinfo 插件:如何将编译时生效的 java 版本添加到生成的 BuildInfo.scala 中? - sbt-buildinfo plugin: How can I add the java version in effect at compile time to the generated BuildInfo.scala? 如何在Scala中将秒转换为时间? - How can I convert seconds to time in Scala?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM