Scala:如何指定暗示相等的类型参数边界?

发布于 2024-12-11 15:23:20 字数 2479 浏览 0 评论 0原文

不要被长文本吓倒,这些要点非常琐碎,但需要一些代码来说明问题。 :-)

设置:

假设我想创建一个特征,这里建模为某种Converter,它本身是通用的,但有一个类型化方法Convert() 返回一个适当类型的结果对象,例如 Container[T]

 trait Converter {
   def convert[T]( input: T ) : Container[T]
 } 
 trait Container[T]  // details don't matter

我的问题是关于方法的类型约束,特别是强制相等性,并且有两个密切相关的部分。

第 1 部分:现在说有一种专门的容器类型特别适合基于数组的内容,如下所示:

 object Container {
   trait ForArrays[U] extends Container[Array[U]] 
 }

考虑到这种可能性,我现在想专门化转换器,特别是返回convert() 方法的类型,到专门的 Container.ForArrays 类型:

 object Converter {
   trait ForArrays extends Converter {
     // the following line is rubbish - how to do this right?
     def convert[E,T <: Array[E]]( input: T ) : Container.ForArrays[E]
   } 
 }

这样我就可以做这样的事情:

val converter = new Converter.ForArrays { ... }
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

基本上我想要 Scala,如果转换器的类型是已知是Converter.ForArrays,还将 convert[Char]() 的专门返回类型推断为 Container.ForArrays[Char],即匹配容器类型加上输入的数组类型。这或类似的事情可能吗?如果可以,我该怎么做?例如,如何指定 Convert() 上的类型参数/界限(提供的只是一个替代品 - 我不知道如何做到这一点)。哦,当然,它仍然重写它的 super 方法,否则什么也得不到。

第 2 部分: 作为后备方案,如果这不可能,我当然可以将转换函数推入以数组为中心的变体中,如下所示:

 trait Converter   // now pretty useless as a shared trait
 object Converter {
   trait ForValues extends Converter { 
     def convert[T]( input: T ) : Container[T] 
   }
   trait ForArrays extends Converter {
     def convert[E]( input: Array[E] ) : Container.ForArrays[E]
   } 
 }

好的。现在假设我有一个更专业的 Converter.ForArrays.SetBased ,它可以在内部使用一组 E 类型的元素(与“输入”数组元素类型相同)在转换。然而,该集合现在是特征的一个参数,如下所示:

 case class SetBased( set: Set[F] ) extends Converter.ForArrays {
   // the following line is also rubbish...
   def convert[E = F]( input: Array[E] ) : Container.ForArrays[E] = {...}
 }

同样,这是关于 Convert() 方法的类型参数。这里的困难是:如何将类的类型参数 - F - 粘合到方法的类型参数 - E - 这样 Scala 编译器只会让用户使用其元素与集合的元素匹配的数组调用 convert()?例子:

val set = Set( 'X', 'Y', 'Z' )
val converter = new Converter.ForArrays.SetBased( set )
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

Don't be put off by the long text, the points are quite trivial but require a bit of code to illustrate the problem. :-)

The Setup:

Say I would like to create a trait, here modeled as a Converter of some kind, that itself is generic but has a typed method convert() that returns an appropriately typed result object, say a Container[T]:

 trait Converter {
   def convert[T]( input: T ) : Container[T]
 } 
 trait Container[T]  // details don't matter

My question is about type constraints on methods, in particular for enforcing equality, and has two closely related parts.

Part 1: Say now that there was a specialized container type that was particularly suitable for array-based contents, like so:

 object Container {
   trait ForArrays[U] extends Container[Array[U]] 
 }

Given this possibility, I'd now like to specialize the Converter and in particular the return type of the convert() method, to the specialized Container.ForArrays type:

 object Converter {
   trait ForArrays extends Converter {
     // the following line is rubbish - how to do this right?
     def convert[E,T <: Array[E]]( input: T ) : Container.ForArrays[E]
   } 
 }

So that I can do something like this:

val converter = new Converter.ForArrays { ... }
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

Basically I want Scala, if the type of converter is known to be Converter.ForArrays, to also infer the specialized return type of convert[Char]() as Container.ForArrays[Char], i.e. the matching container type plus the array type of the input. Is this or something like it possible and if so, how do I do it? E.g. how do I specify the type parameters / bounds on convert() (what is provided is just a stand-in - I have no idea how to do this). Oh, and naturally so that it still overrides its super method, otherwise nothing is gained.

Part 2: As a fallback, should this not be possible, I could of course push the convert function down into the Array-focused variant, like so:

 trait Converter   // now pretty useless as a shared trait
 object Converter {
   trait ForValues extends Converter { 
     def convert[T]( input: T ) : Container[T] 
   }
   trait ForArrays extends Converter {
     def convert[E]( input: Array[E] ) : Container.ForArrays[E]
   } 
 }

OK. Now say I have an even more specialized Converter.ForArrays.SetBased that can internally use a set of elements of type E (the same as the 'input' array element type) to do some particular magic during the conversion. The set is now a parameter of the trait, however, like so:

 case class SetBased( set: Set[F] ) extends Converter.ForArrays {
   // the following line is also rubbish...
   def convert[E = F]( input: Array[E] ) : Container.ForArrays[E] = {...}
 }

Again, this is about the type parameters of the convert() method. The difficulty here is: how do I glue the type parameter of the class - F - to the type parameter of the method - E - such that the Scala compiler will only let the user call convert() with an array whose elements match the elements of the set? Example:

val set = Set( 'X', 'Y', 'Z' )
val converter = new Converter.ForArrays.SetBased( set )
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ヤ经典坏疍 2024-12-18 15:23:20

不,你不能。出于同样的原因,您在重写方法时不能缩小参数类型或扩大返回类型(但可以缩小返回类型)。但是,您可以执行以下操作(对于您的后备解决方案):

trait Converter {
  type Constraint[T]
} 

trait ForArrays extends Converter {
  def convert[E]( input: Array[E] )( implicit ev : Constraint[T] ) : Container.ForArrays[E]
}

case class SetBased[F](set: Set[F]) extends Converter {
   type Constraint[T] = T =:= F
   def convert[E]( input: Array[E] )( implicit ev : E =:= F ) = ...
} 

No, you can't. For the same reason you can't narrow argument types or widen return types when overriding a method (but can narrow return type). Here is what you can do, however (for your fallback solution):

trait Converter {
  type Constraint[T]
} 

trait ForArrays extends Converter {
  def convert[E]( input: Array[E] )( implicit ev : Constraint[T] ) : Container.ForArrays[E]
}

case class SetBased[F](set: Set[F]) extends Converter {
   type Constraint[T] = T =:= F
   def convert[E]( input: Array[E] )( implicit ev : E =:= F ) = ...
} 
人生戏 2024-12-18 15:23:20

我假设 Container.ForArrays 是 Container 的子类,没有这个,Converter.ForArrays.convert 将不匹配被覆盖的 Converter.convert 的签名

尝试编写如下内容:

object Converter {
  trait ForArrays extends Converter {
    def convert[E] (input: Array[E]): Container.ForArrays[E]
  } 
}

关于您的后备解决方案。如果两个类型相同,则只需使用相同的类型参数!

case class SetBased (set: Set[F]) extends Converter.ForArrays {
  def convert (input: Array[F]): Container.ForArrays[F] = {...}
}

I'm going to assume that Container.ForArrays is a subclass of Container, without this, Converter.ForArrays.convert won't match the signature of the overridden Converter.convert

Try writing it something like this:

object Converter {
  trait ForArrays extends Converter {
    def convert[E] (input: Array[E]): Container.ForArrays[E]
  } 
}

Regarding your fallback solution. If two types are the same, then just use the same type param!

case class SetBased (set: Set[F]) extends Converter.ForArrays {
  def convert (input: Array[F]): Container.ForArrays[F] = {...}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文