关于通用特征的隐含转换

时间:2011-01-28 14:11:08

标签: scala generics implicit traits

我正在实现一个数据结构,并希望用户能够使用任何类型作为键,只要他提供一个合适的密钥类型包装它。我有这个关键类型的特征。我们的想法是从基类到键类型进行隐式转换,另一种方法是(实际上)只使用基类型。特征看起来像这样:

trait Key[T] extends Ordered[Key[T]] {
  def toBase : T

  // Further stuff needed for datastructure...
}
object Key {
  implicit def key2base[T](k : Key[T]) : T = k.toBase
}

呼叫站点代码可能如下所示:

def foo[K <% Key[K]]( bar : Seq[K] ) = bar.sorted(0)

计划是K类型的值应该隐式转换为有序的Key[K],或者Key[K]上的排序应该分别使用,所以一切都应该解决。当然,没有办法在特征本身中实现隐式base2key。或者在那里,也许使用隐式传递的类清单?考虑到这一点,我找不到任何参考。

是否有可能以某种方式断言断言任何延伸Key[T]的类型都会带有隐式转换T => Key[T]?遗憾的是,伴侣对象不能有抽象方法。

假设这样做,整个企业是否可行,或者所述用例是否需要多个链式隐式转换? (正如我所读到的那样,链接不会发生。)

附录:根据上述定义,我可以使用Node(key : K, ...)K <% Key[K]sortWith(_.key <= _.key)下)的序列进行排序,但不使用sortBy(_.key) }。显然,从KKey[K]的转换是隐式发生的,即使我从未在任何地方声明它,但Ordering上没有隐含的Key[K]。这是怎么回事?

3 个答案:

答案 0 :(得分:1)

你问“是否有可能以某种方式静态断言任何扩展Key[T]的类型都会带有隐式转换T => Key[T]?伴随对象不能有抽象方法,遗憾的是。”

但是你的例子是一个静态断言:当你需要从TKey[T]的视图时,你在编译时断言你只能调用foo来获取可以解除的类型钥匙。或者我误解了什么?

关于附录:你说你感到惊讶的是“从KKey[K]的转换是隐含的,即使它从未在任何地方被我宣布过”。事情是,你确实声明了它:T <% Key[T](我在这里使用的是T而不是K,你似乎混淆了基础* T 的概念* ype和* K *在这里?)。这与

相同
def foo[T](bar : Seq[T])(implicit view: T => Key[T]) = bar.sortWith(_ <= _)(0)

因此,当您执行sortWith(_ <= _)时,您将T强制转换为Key[T](根据特征<=定义Ordered[Key[T]])。

以你的Node为例,为什么不这样做

case class Node[K](key: K)
def test[K](bar: Seq[Node[K]])(implicit ord: Ordering[K]) = bar.sortBy(_.key)(0)

希望有所帮助...

答案 1 :(得分:0)

以下是您问题的可能解决方案(希望我能正确理解您的要求):

// Key stuff

trait Keyable[T] {
  def toKey(t: T): Key[T]
}

trait Key[T] extends Ordered[Key[T]] {
  def toBase() : T
}

object Key {
  implicit def key2base[T](k : Key[T]) : T = k.toBase
  implicit def base2key[T : Keyable](k : T) : Key[T] = implicitly[Keyable[T]].toKey(k)
}

// more concrete stuff - namely A

class A(val i: Int) {
  override def toString = "A {" + i + "}"
}

object A {
  implicit val aKeyable = new Keyable[A] {
    def toKey(t: A) = new Key[A] {
      def toBase() = t
      def compare(that: Key[A]) = t.i compare that.i
    }
  }
}

// testing

def foo[K : Keyable](bar: Seq[K]) = bar.sortBy(implicitly[Keyable[K]].toKey)

val list = List(new A(5), new A(1), new A(3))

println(foo(list)) // prints: List(A {1}, A {3}, A {5})

在这种情况下,Key[T]是类型T的包装,而Keyable[T]是类型calss,允许将类型T转换为Key[T]。我还展示了base2key的样子。

答案 2 :(得分:0)

在这个答案中,我将保留目前最好的版本以供参考。使用this answer更集中的问题;将根据this one以2.9的形式过时。

Key特征保持不变;我添加了一个特定的功能用于说明:

trait Key[T] extends Ordered[Key[T]] {
  def toBase : T
  def foo(i : Int) : Key[T]
}
object Key {
  implicit def key2base[T](k : Key[T]) : T = k.toBase
  implicit def ordering[T <% Key[T]] = new Ordering[T]{
    def compare(x: T, y: T) = x compare y
  }
}

以下按预期工作(如果import Key._已完成):

def min[K <% Key[K]](l : Seq[K]) : K = l.sorted.head

我们假设我们有一个简单的class Node[K](val key : K)。事情再次发挥作用:

def min[K <% Key[K]](l : Seq[Node[K]]) : Node[K] = l.sortBy(_.key).head

再举一个例子,假设这段代码只使用Key[T]接口:

def test[K <% Key[K]](bar : Seq[K]) = 
  bar.map(_.foo(3)).sorted

请注意,由于map直接产生Seq[Key[K]],因此会进行编译;排序不需要转换。现在,如果我们正确实施Key,请说

class StringKey(val key : String) extends Key[String] {
  def foo(i : Int) =  StringKey(this.key + "_" + i)
  def toBase = key
  override def compare(other : Key[String]) = -1*this.key.compare(other.toBase)
}
object StringKey {
  def apply(key : String) = new StringKey(key)
  def unapply(key : String) = Some(key)
  implicit def string2key(s : String) = StringKey(s)
}

以下内容应该有效:

import StringKey.string2key
import Key.key2base
val bla : Seq[String] = test(Seq("b", "c", "a")).map(_.capitalize)
println(bla)
// expected output: Seq(C_3, B_3, A_3)

但实际上,找不到从StringKeyString的转换:

error: value capitalize is not a member of this.Key[java.lang.String]

这很奇怪; Key[String]String的转换,如果使用泛型类型参数声明的话。