Dotty无法使用抽象类型来推断带有类型参数特征的通用Scala函数的结果类型

d

一个简单的价值层次

想象一下这个简单的特征Value,其中每个实现类都具有value某种类型T

trait Value {
  type T
  def value: T
}

我们有两个不同的实现类IntString分别表示

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
}

键入值的安全选择

如果我们有一个List值,我们希望有一种类型安全的方式来选择特定类型的所有值。Values及其配套对象可以帮助我们做到这一点:

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
  }
}

所有这些在Scala 2.13和Dotty 0.20.0-RC1中都可以正常工作,因此具有混合值列表…

val valueList = List(IntValue(1), StringValue("hello"))
val values = Values(valueList)

…我们可以选择元素并将它们返回为正确的类型-在编译时全部检查:

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]

Option[T]在Dotty中选择失败的值

但是,如果我们添加此函数以选择值作为其T类型(即IntString)并将其返回为Option

class Values ... {
  ...
  def getValueInnerOption[V <: Value : ClassTag] = {
    getValues.headOption.map(_.value)
  }
}

…然后在Scala 2.13中一切正常:

val iOption: Option[Int] = values.getValueInnerOption[IntValue]
val sOption: Option[String] = values.getValueInnerOption[StringValue]

但是在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)
}

不幸的是,这意味着我们将不得不在呼叫站点添加实际的类型T(即IntString),这很遗憾,因为它只是样板。

val iOption: Option[Int] = values.getValueInnerOption[IntValue, Int]
val sOption: Option[String] = values.getValueInnerOption[StringValue, String]

Dotty中的错误或怎么办?

看起来Dotty已经知道什么是上限,T但是无法将该知识传播到函数的结果类型。由此可以看出,如果试图问一个String来自IntValue

-- [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} 

那么原始代码(不带type参数U)是可以在最终Scala 3.0中使用的东西吗,还是需要以其他方式编写?

米米(Dmytro Mitin)

在Dotty中,尝试将匹配类型替换为类型投影

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)
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Scala:使用类型参数或抽象类型作为类型界限

来自分类Dev

构造函数结果类型不是带有类型参数的类上的<:<类类型

来自分类Dev

(在Scala中)可以使用类的泛型类型参数来完成,而不能使用抽象类型成员来完成吗?

来自分类Dev

为什么Scala编译器不使用抽象类型来推断类型

来自分类Dev

引用类型参数的抽象类型成员

来自分类Dev

引用抽象类型成员隐藏的通用类型参数

来自分类Dev

无法推断有关_的足够类型信息;需要类型注释或通用参数绑定

来自分类Dev

当使用带有IEnumerable值的Dictionary时,无法从用法中推断出方法的类型参数

来自分类Dev

T必须是具有公共无参数构造函数的非抽象类型,才能在通用类型或方法中将其用作参数“ TModel”

来自分类Dev

参数局部抽象类型

来自分类Dev

函数类型的Scala类型推断

来自分类Dev

Scala中通用抽象类型的模式匹配

来自分类Dev

从单个构造函数参数推断通用类类型参数

来自分类Dev

在Scala中推断通用类型参数

来自分类Dev

Scala Akka:使用子类型参数实现抽象类

来自分类Dev

为什么Scala无法为具有多个参数列表的函数推断通用返回类型?

来自分类Dev

通用方法无法推断类型

来自分类Dev

Dotty无法使用抽象类型来推断带有类型参数特征的通用Scala函数的结果类型

来自分类Dev

混合通用特征的类型推断

来自分类Dev

在带有Swift的函数中使用类型擦除返回通用类型(无法转换类型的返回表达式…)

来自分类Dev

无法推断通用类的类型参数

来自分类Dev

Rust无法为通用特征impl推断返回类型

来自分类Dev

抽象类的通配符类型推断

来自分类Dev

构造函数结果类型不是带有类型参数的类上的<:<类类型

来自分类Dev

Scala:带有参数化函数类型

来自分类Dev

引用由抽象类型成员隐藏的通用类型参数

来自分类Dev

Scala抽象类型成员

来自分类Dev

为什么在通用函数类型参数推断中,Scala无法将Null类型识别为T的子类型?

来自分类Dev

使用具有抽象类型的 Scala 数组

Related 相关文章

  1. 1

    Scala:使用类型参数或抽象类型作为类型界限

  2. 2

    构造函数结果类型不是带有类型参数的类上的<:<类类型

  3. 3

    (在Scala中)可以使用类的泛型类型参数来完成,而不能使用抽象类型成员来完成吗?

  4. 4

    为什么Scala编译器不使用抽象类型来推断类型

  5. 5

    引用类型参数的抽象类型成员

  6. 6

    引用抽象类型成员隐藏的通用类型参数

  7. 7

    无法推断有关_的足够类型信息;需要类型注释或通用参数绑定

  8. 8

    当使用带有IEnumerable值的Dictionary时,无法从用法中推断出方法的类型参数

  9. 9

    T必须是具有公共无参数构造函数的非抽象类型,才能在通用类型或方法中将其用作参数“ TModel”

  10. 10

    参数局部抽象类型

  11. 11

    函数类型的Scala类型推断

  12. 12

    Scala中通用抽象类型的模式匹配

  13. 13

    从单个构造函数参数推断通用类类型参数

  14. 14

    在Scala中推断通用类型参数

  15. 15

    Scala Akka:使用子类型参数实现抽象类

  16. 16

    为什么Scala无法为具有多个参数列表的函数推断通用返回类型?

  17. 17

    通用方法无法推断类型

  18. 18

    Dotty无法使用抽象类型来推断带有类型参数特征的通用Scala函数的结果类型

  19. 19

    混合通用特征的类型推断

  20. 20

    在带有Swift的函数中使用类型擦除返回通用类型(无法转换类型的返回表达式…)

  21. 21

    无法推断通用类的类型参数

  22. 22

    Rust无法为通用特征impl推断返回类型

  23. 23

    抽象类的通配符类型推断

  24. 24

    构造函数结果类型不是带有类型参数的类上的<:<类类型

  25. 25

    Scala:带有参数化函数类型

  26. 26

    引用由抽象类型成员隐藏的通用类型参数

  27. 27

    Scala抽象类型成员

  28. 28

    为什么在通用函数类型参数推断中,Scala无法将Null类型识别为T的子类型?

  29. 29

    使用具有抽象类型的 Scala 数组

热门标签

归档