1

我正在寻找一种从以下示例中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到成员字段。

4

4 回答 4

3

您是否考虑过利用unapply隐含的ClassTag?如果我正确理解了文档,则unapplyfrom 标记将返回 None 如果attr不完全匹配其类型。如果匹配,它将返回 Some of type A[S]。因此重构为使用 unapply,您的代码将如下所示:

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
  }
于 2013-05-11T23:11:45.353 回答
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
    }
}

请注意,现在Atype 参数是更高阶的类型。因此,在apply方法中的每次出现时,您都需要“应用”A到另一种类型以获得正确的类型。

这个问题为高阶类型提供了更多见解:

Scala 中的高级类型是什么?

于 2013-05-04T19:32:33.927 回答
1

单程。请注意,将任何类型称为 Int 需要一定的胆量,即使是嵌套类型。

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
}
于 2013-05-12T00:22:39.033 回答
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
于 2013-05-12T08:25:35.277 回答