我正在寻找一种方法,可以在以下示例中从S
的调用中删除类型参数apply
:
object Attribute {
trait Int [S] extends Attribute[S]
trait Boolean[S] extends Attribute[S] // etc.
}
sealed trait Attribute[S]
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A <: Attribute[S]](key: String)
(implicit tag: reflect.ClassTag[A]): Option[A] =
get(key) match {
case Some(attr: A) => Some(attr)
case _ => None
}
}
根据上述定义,测试用例将是:
trait Test[S] {
def map: Attributes[S]
map[Attribute.Int[S]]("foo")
}
我正在尝试做的是修改apply
定义以允许以下内容:
trait Test2[S] {
def map: Attributes[S]
map[Attribute.Int]("foo") // use partially applied attribute type
}
编辑:所以跟进Marius的建议和评论,为什么以下仍然会产生删除警告:
import reflect.runtime.universe._
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A[x] <: Attribute[x]](key: String)
(implicit tag: TypeTag[A[S]]): Option[A[S]] =
get(key) match {
case Some(attr: A[S]) => Some(attr)
case _ => None
}
}
对我来说显然没有意义。一方面,我有A[S]
的完整类型标签。另一方面,它应该在缺席时完全工作,因为Attribute
中S
是不变的,所以如果我得到Option[Attribute[S]]
,我与Some(attr: A[x])
匹配,唯一的可能是x == S
。
编辑2 :解决方案的条件是Attribute
特征的形状不会改变,例如不将类型构造函数参数S
移动到成员字段。
答案 0 :(得分:3)
您是否考虑过利用隐含unapply
的{{1}}?如果我正确理解文档,如果ClassTag
与其类型不完全匹配,则标记中的unapply
将返回“无”。如果匹配,则会返回某些类型attr
。所以重构使用unapply,你的代码看起来像这样:
A[S]
答案 1 :(得分:1)
我认为你想要更高级的类型。我重写了你的代码:
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A[_] <: Attribute[_]](key: String)
(implicit tag: reflect.ClassTag[A[S]]): Option[A[S]] =
get(key) match {
case Some(attr: A[S]) => Some(attr)
case _ => None
}
}
请注意,现在A
类型参数是更高阶的类型。因此,在apply
方法中的每次出现时,您都需要将A
“应用”到另一种类型以获得正确的类型。
此问题为更高阶类型提供了更多见解:
答案 2 :(得分:1)
一种方式。请注意,调用任何类型的Int,即使是嵌套的Int也需要一定的chutzpah。
import scala.reflect._
sealed trait Attribute {
type S
}
object Attribute {
trait Ints extends Attribute { type S = Int }
}
/** A container for attributes of a certain type.
*/
trait Attributes[E] {
type Attr = Attribute { type S <: E }
protected def get(key: String): Option[Attr]
/** Retrieve the value for the given key.
* @tparam A must be our notion of Attribute
* @return optionally the A you asked for
*/
def apply[A <: Attr](key: String)(implicit tag: ClassTag[A]): Option[A] =
get(key) match {
case Some(attr: A) => Some(attr)
case _ => None
}
}
trait Test2 {
/** Map keys to ints. */
def map: Attributes[Int]
/** Retrieve a value. */
map[Attribute.Ints]("foo") // use partially applied attribute type
}
答案 3 :(得分:0)
这只是为了表明@cmbaxter的答案按预期工作。我还将重述初始代码,以便它完全可执行:
// 属性界面
object Attribute {
trait Int [S] extends Attribute[S]
trait Boolean[S] extends Attribute[S] // etc.
}
sealed trait Attribute[S]
// 属性地图界面
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A[_] <: Attribute[_]](key: String)
(implicit tag: reflect.ClassTag[A[S]]): Option[A[S]] =
get(key) match {
case Some(attr) => tag.unapply(attr)
case _ => None
}
}
}
// 测试用例
class Test {
val map = new Attributes[Foo] {
def get(key: String) = key match {
case "foo" => Some(new Attribute.Int [Foo] { override def toString = "I" })
case "bar" => Some(new Attribute.Boolean[Foo] { override def toString = "B" })
case _ => None
}
}
println(s"An int attr named `foo`: ${map[Attribute.Int ]("foo")}")
println(s"A bool attr named `foo`: ${map[Attribute.Boolean]("foo")}")
println(s"An int attr named `bar`: ${map[Attribute.Int ]("bar")}")
println(s"A bool attr named `bar`: ${map[Attribute.Boolean]("bar")}")
println(s"An int attr named `???`: ${map[Attribute.Int ]("???")}")
println(s"A bool attr named `???`: ${map[Attribute.Boolean]("???")}")
}
// 正在运行
new Test
An int attr named `foo`: Some(I)
A bool attr named `foo`: None
An int attr named `bar`: None
A bool attr named `bar`: Some(B)
An int attr named `???`: None
A bool attr named `???`: None