如何将traits声明为隐含的“构造函数参数”?

时间:2011-08-08 14:35:58

标签: scala implicit traits

我正在设计一个类层次结构,它由一个基类和几个特征组成。基类提供了几种方法的默认实现,并且traits通过abstract override选择性地覆盖某些方法,以便充当可堆叠的traits / mixins。

从设计的角度来看,这很有效,并且映射到域,这样我就可以从这里添加一个过滤函数(一个特征)和一个来自这里的谓词(另一个特征)等。

但是,现在我想要一些隐藏参数的特性。我很高兴从设计的角度来看这仍然有意义,并且在实践中不会让人感到困惑。但是,我无法说服编译器运行它。

问题的核心似乎是我无法为特征提供构造函数参数,因此可以将它们标记为隐式。在方法实现中引用隐式参数无法使用预期的“无法找到隐式值”消息进行编译;我试图通过

在构造阶段(实际上,它始终在范围内)“隐藏”隐含的方法。
implicit val e = implicitly[ClassName]

但是(毫无疑问,你们很多人都希望) 定义失败并带有相同的消息。

这里的问题似乎是我无法说服编译器使用implicit ClassName标记来标记特征本身的签名,并强制调用者(即将特征混合到对象中的那些人)提供隐含的。目前我的调用者正在这样做,但编译器没有在此级别进行检查。


有没有办法将特征标记为需要在施工时提供某些暗示?

(如果没有,这还没有实现,还是有更深层次的理由说明这是不切实际的?)

5 个答案:

答案 0 :(得分:16)

实际上,我之前经常想要这个,但只想出了这个想法。你可以翻译

trait T(implicit impl: ClassName) {
  def foo = ... // using impl here
}

到[EDITED:原始版本没有为其他方法提供隐式访问]

trait T {
  // no need to ever use it outside T
  protected case class ClassNameW(implicit val wrapped: ClassName)

  // normally defined by caller as val implWrap = ClassNameW 
  protected val implWrap: ClassNameW 

  // will have to repeat this when you extend T and need access to the implicit
  import implWrap.wrapped

  def foo = ... // using wrapped here
}

答案 1 :(得分:11)

我遇到过这个问题几次,确实有点烦人,但不是太多。摘要成员和参数通常是做同样事情的两种替代方式,各有利弊;对于具有抽象成员的特征并不太不方便,因为无论如何你还需要另一个类来实现这个特征。*

因此,您应该在特征中简单地使用抽象值声明,以便实现类必须为您提供隐式。请参阅以下示例 - 它正确编译,并显示了实现给定特征的两种方法:

trait Base[T] {
    val numT: Ordering[T]
}
/* Here we use a context bound, thus cannot specify the name of the implicit
 * and must define the field explicitly.
 */
class Der1[T: Ordering] extends Base[T] {
    val numT = implicitly[Ordering[T]]
    //Type inference cannot figure out the type parameter of implicitly in the previous line
}
/* Here we specify an implicit parameter, but add val, so that it automatically
 * implements the abstract value of the superclass.
 */
class Der2[T](implicit val numT: Ordering[T]) extends Base[T]

我展示的基本想法也出现在Knut Arne Vedaa的回答中,但我试图制作一个更引人注目且更方便的例子,不再使用不需要的功能。

*这不是特性不能接受参数的原因 - 我不知道。我只是在争论在这种情况下这种限制是可以接受的。

答案 2 :(得分:11)

这是不可能的。

但您可以使用implicitly和Scala的类型推断来尽可能轻松地进行此操作。

trait MyTrait {

    protected[this] implicit def e: ClassName

}

然后

class MyClass extends MyTrait {

    protected[this] val e = implicitly // or def

}

简洁,甚至不需要在扩展类中编写类型。

答案 3 :(得分:0)

你可以这样做:

abstract class C

trait A { this: C =>
    val i: Int
}    

implicit val n = 3

val a = new C with A {
    val i = implicitly[Int]
}

但是我不确定它是否有任何意义 - 你也可以明确地引用隐含值。

我想你想要的是在实例化中摆脱i的实现,但正如你自己所说,问题的核心是traits不接受构造函数参数 - 无论它们是否会隐含与否并不重要。

此问题的可能解决方案是为已有效的语法添加新功能:

trait A {
    implicit val i: Int
}

如果隐式在范围内,编译器将实现i

答案 4 :(得分:0)

因为看起来这是不可能的,所以我选择在基类的构造函数上声明隐式val。正如问题所指出的那样,这并不理想,但它满足了编译器,并且在实际情况下,在我的特定情况下并没有太大的负担。

如果有人有更好的解决方案,我会很高兴听到并接受它。