Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/155.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dotty无法推断采用抽象类型的类型参数trait的泛型Scala函数的结果类型 简单的值层次结构_Scala_Dependent Type_Dotty_Type Projection_Scala 3 - Fatal编程技术网

Dotty无法推断采用抽象类型的类型参数trait的泛型Scala函数的结果类型 简单的值层次结构

Dotty无法推断采用抽象类型的类型参数trait的泛型Scala函数的结果类型 简单的值层次结构,scala,dependent-type,dotty,type-projection,scala-3,Scala,Dependent Type,Dotty,Type Projection,Scala 3,想象一下这个简单的trait值,其中每个实现类都有一个类型为T的值 我们有两个不同的实现类,分别表示Int和String值 case class IntValue(override val value: Int) extends Value { override type T = Int } case class StringValue(override val value: String) extends Value { override type T = String } 值的类

想象一下这个简单的trait值,其中每个实现类都有一个类型为T的值

我们有两个不同的实现类,分别表示Int和String值

case class IntValue(override val value: Int) extends Value {
  override type T = Int
}

case class StringValue(override val value: String) extends Value {
  override type T = String
}
值的类型安全选择 如果我们有一个值列表,我们希望有一个类型安全的方法来选择特定类型的所有值。类值及其伴生对象帮助我们做到这一点:

object Values {
  private type GroupedValues = Map[ClassTag[_ <: Value], List[Value]]

  def apply(values: List[Value]): Values = {
    val groupedValues: GroupedValues = values.groupBy(value => ClassTag(value.getClass))
    new Values(groupedValues)
  }
}

class Values private (groupedValues: Values.GroupedValues) {
  // Get a List of all values of type V.
  def getValues[V <: Value : ClassTag] = {
    val classTag = implicitly[ClassTag[V]]
    groupedValues.get(classTag).map(_.asInstanceOf[List[V]]).getOrElse(Nil)
  }

  def getValue[V <: Value : ClassTag] = {
    getValues.head
  }

  def getValueOption[V <: Value : ClassTag] = {
    getValues.headOption
  }

  def getValueInner[V <: Value : ClassTag] = {
    getValues.head.value
  }
}
…我们可以选择元素并将其作为正确的类型返回–所有元素都在编译时检查:

val ints: List[IntValue] = values.getValues[IntValue]
val strings: List[StringValue] = values.getValues[StringValue]

val int: IntValue = values.getValue[IntValue]
val string: StringValue = values.getValue[StringValue]

val intOption: Option[IntValue] = values.getValueOption[IntValue]
val stringOption: Option[StringValue] = values.getValueOption[StringValue]

val i: Int = values.getValueInner[IntValue]
val s: String = values.getValueInner[StringValue]
在Dotty中选择一个值作为选项[T]失败 但是,如果我们添加此函数以选择值作为其T类型,即Int和String,并将其作为选项返回

但在Dotty 0.20.0-RC1中,这并不编译:

-- [E007] Type Mismatch Error: getValue.scala:74:29 
74 |  val iOption: Option[Int] = values.getValueInnerOption[IntValue]
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                             Found:    Option[Any]
   |                             Required: Option[Int]
-- [E007] Type Mismatch Error: getValue.scala:75:32 
75 |  val sOption: Option[String] = values.getValueInnerOption[StringValue]
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                                Found:    Option[Any]
   |                                Required: Option[String]
我们可以通过向getValueInnerOption添加一个类型参数来解决这个问题,该参数将返回类型和抽象类型T绑定在一起,并允许我们指定返回类型

def getValueInnerOption[V <: Value {type T = U} : ClassTag, U]: Option[U] = {
  getValues.headOption.map(_.value)
}
Dotty中的一个bug或者怎么办? Dotty似乎已经知道T的上界是什么,但无法将该知识传播到函数的结果类型。如果试图从IntValue请求字符串,则可以看到:

那么,不带类型参数U的原始代码是否可以在最终的Scala 3.0中正常工作,或者是否需要以其他方式编写?

\u0。值具有默认情况下不推断的值,但您可以指定它:

def getValueInnerOption[V <: Value : ClassTag] = {
  getValues.headOption.map((_.value): (v: V) => v.T)
}
编译

但问题是,我不确定它和getValueInner是否应该工作。因为它们的推断返回类型涉及VT,如果您给出错误的返回类型,并且尝试显式指定它们,则可以在错误消息中看到它们

V不是一个合法的路径,因为它不是一个具体的类型

请参见Dotty try中的

以替代类型投影

type InnerType[V <: Value] = V match {
  case IntValue    => Int
  case StringValue => String
}

trait Value {
  type This >: this.type <: Value
  type T = InnerType[This]
  def value: T
}

case class IntValue(override val value: Int) extends Value {
  override type This = IntValue
}

case class StringValue(override val value: String) extends Value {
  override type This = StringValue
}

def getValueInner[V <: Value { type This = V } : ClassTag]: InnerType[V] = {
  getValues.head.value
}

def getValueInnerOption[V <: Value { type This = V } : ClassTag]: Option[InnerType[V]] = {
  getValues.headOption.map(_.value)
}

谢谢你的回复。是的,我还注意到在REPL中,getValueInner的结果类型实际上解析为一个类型投影VT,我认为它在Dotty中不存在——如果您试图将返回类型显式声明为类型保护,则会出现上面写的错误。如果您的解决方案不能像您所说的那样在未来的Dotty版本中可能无法工作,那么有没有其他方法可以实现我想要的呢,Dotty将返回类型解析为投影VT的原因不是吗?map对集合进行操作称之为C值在这种情况下只有一个,因为C是选项,因此map的返回类型不能是C[v.t]因为可能有多个值,例如,对于一个列表,所以许多v的v.T的常见超类型是VT。因为T的IntValue为Int有一个上限,这与将其分配给声明为选项[Int]的val是兼容的。如果没有与两种类型参数方法等效的方法,我认为不会这样做,但我对Dotty了解得相对较少。@mgd问题是,VT在Dotty中首先不应该是合法的,因此超类型应该是ValueT,它是合法的,甚至是任意的。这就是在推断u.value的类型时发生的情况,它为您提供了选项[Any].优雅的解决方案。我能看到的唯一警告是,每当向值层次结构添加新类型时,都需要更新InnerType。所以在实践中,trait值将被密封,因为只有在可以修改InnerType所在的源文件时,才能添加子类。
-- [E007] Type Mismatch Error: getValue.scala:74:29 
74 |  val iOption: Option[Int] = values.getValueInnerOption[IntValue]
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                             Found:    Option[Any]
   |                             Required: Option[Int]
-- [E007] Type Mismatch Error: getValue.scala:75:32 
75 |  val sOption: Option[String] = values.getValueInnerOption[StringValue]
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                                Found:    Option[Any]
   |                                Required: Option[String]
def getValueInnerOption[V <: Value {type T = U} : ClassTag, U]: Option[U] = {
  getValues.headOption.map(_.value)
}
val iOption: Option[Int] = values.getValueInnerOption[IntValue, Int]
val sOption: Option[String] = values.getValueInnerOption[StringValue, String]
-- [E057] Type Mismatch Error: getValue.scala:75:39 
75 |  val wtf = values.getValueInnerOption[IntValue, String]
   |                                       ^
   |Type argument IntValue does not conform to upper bound Value{T = String} 
def getValueInnerOption[V <: Value : ClassTag] = {
  getValues.headOption.map((_.value): (v: V) => v.T)
}
val iOption: Option[Int] = values.getValueInnerOption[IntValue]
val sOption: Option[String] = values.getValueInnerOption[StringValue]
type InnerType[V <: Value] = V match {
  case IntValue    => Int
  case StringValue => String
}

trait Value {
  type This >: this.type <: Value
  type T = InnerType[This]
  def value: T
}

case class IntValue(override val value: Int) extends Value {
  override type This = IntValue
}

case class StringValue(override val value: String) extends Value {
  override type This = StringValue
}

def getValueInner[V <: Value { type This = V } : ClassTag]: InnerType[V] = {
  getValues.head.value
}

def getValueInnerOption[V <: Value { type This = V } : ClassTag]: Option[InnerType[V]] = {
  getValues.headOption.map(_.value)
}