1

在下面的程序中,我试图让一个类型类工作。类型类是 Algo,它的实际实现需要一个 Container[_ <: Id] 并且应该从该容器返回一个元素。但是由于某种原因,抽象特征中算法方法的返回类型和实现函数不同,即使它们应该是相同的。此外,由于某种原因,未找到隐式 def。任何人都可以阐明我做错了什么?

class Element {
}
trait Container[T <: Element] {
    type ElemType = T
    val value : T
}

trait Id {
}

class ElementWithId extends Element with Id

trait Algo[T <: Container[_]] {
    def algo(t : T) : Option[T#ElemType]
}

object Algo {
    implicit def impl[C <: Container[_ <: Id]] = new Algo[C] {
        def algo(cont : C) : Option[C#ElemType] = {
            Some(cont.value)
        }
    }

    implicitly[Algo[Container[ElementWithId]]].algo(new Container[ElementWithId] {
                                                  override val value = new ElementWithId
                                              })
}

完整的错误信息是:

[error] test.scala:126: type mismatch;
[error]  found   : cont.value.type (with underlying type _$2)
[error]  required: _$2
[error]  Note: implicit method impl is not applicable here because it comes after the application point and it lacks an explicit result type
[error]                 Some(cont.value)
[error]                           ^
[error] test.scala:130: could not find implicit value for parameter e: Algo[Container[ElementWithId]]
[error]         implicitly[Algo[Container[ElementWithId]]].algo(new Container[ElementWithId] {
[error]                   ^
[warn] test.scala:124: inferred existential type $anon forSome { type $anon <: Algo[C]{def algo(cont: C): Option[_$2]}; type _$2 <: Id }, which cannot be expressed by wildcards,  should be enabled
[warn] by making the implicit value language.existentials visible.
[warn] This can be achieved by adding the import clause 'import scala.language.existentials'
[warn] or by setting the compiler option -language:existentials.
[warn] See the Scala docs for value scala.language.existentials for a discussion
[warn] why the feature should be explicitly enabled.
[warn]         implicit def impl[C <: Container[_ <: Id]] = new Algo[C] {
[warn]                                                      ^
[warn] one warning found
[error] two errors found
[error] (Ducktank/compile:compile) Compilation failed
[error] Total time: 1 s, completed 25.04.2013 23:22:17

更新

提供的解决方案解决了上述问题,但我忘记指定要求 - 对此我深表歉意。我希望 Algo 独立于容器的种类。有了现在的答案,我必须知道容器类型的类型边界(即 T <: Element),然后基本上添加 Algo 需要的类型边界(即 T <: Id) . 但我想保持它足够具体,它可以在具有不同类型边界的容器上工作(例如,Container[T <: SomeOtherElement],而不是 Container[T <: Element])。这在某种程度上可能吗?

4

2 回答 2

4

您的第一个问题是,在您的 Algo 实现中,您 return value,它被声明为 type T,但期望它是T#Elem(可能,您的意思是cont.Elem,更精确)。类型恰好相同,但编译器不会检查。如果您声明,您可以开始去某个地方val value: ElemType

您通过同时拥有类型参数 T 和类型成员 ElemType 引入了很多复杂性,您应该选择其中一个,要么

trait Container[T <: Element] {
  val value : T // might be wiser to avoid abstract val and prefer abstract def
}

或者

trait Container {
  type Elem  // abstract
  val value: Elem // or def
}

我宁愿选择第一个。有了这个,算法的正确写作将是

trait Algo[C[X <: Element] <: Container[X]] {
  def algo[A <: Element](c: C): Option[A]
}

这假设您可能必须在某个地方传递一个算法,但事先不知道将使用它的容器内容的类型是什么,可能是因为它将与几种不同的类型一起使用。否则,就做更简单的

trait Algo[A <: Element, C <: C[A]] {
  def algo(c: C[A]): Option[A]
}

回到 high-order Algo[C[X] <: Container[X]],您的算法必须在不受容器内容类型限制的情况下工作。如果你想能够限制它,你可以这样做

trait Algo[R <: ElementType, C[X <: R] <: Container[X]] {
   def algo[A <: R](c: C[A]): Option[A]
}

或者

trait Algo4[R, C[X <: Element with R] <: Container[X]] {
  def algo[A <: Element with R] (c: C[A]) : Option[A]
}
于 2013-04-25T22:58:41.037 回答
3

这编译:

trait Algo[T, C <: Container[T]] {
    def algo(t : C) : Option[T]
}

object Algo {
    implicit def impl[T <: Id, C <: Container[T]] = new Algo[T,C] {
        def algo(cont : C) : Option[T] = {
            Some(cont.value)
        }
    }

    implicitly[Algo[ElementWithId,Container[ElementWithId]]].algo(new Container[ElementWithId] {
                                                  override val value = new ElementWithId
                                              })
}

通配符对于编译器来说似乎有些难以处理。所以你可以帮他一把:)

于 2013-04-25T22:14:31.903 回答