简体   繁体   English

如何将特征声明为采用隐式“构造函数参数”?

[英]How to declare traits as taking implicit “constructor parameters”?

I'm designing a class hierarchy, which consists of a base class along with several traits.我正在设计一个类层次结构,它由一个基类和几个特征组成。 The base class provides default implementations of several methods, and the traits selectively override certain methods via abstract override , so as to acts as stackable traits/mixins.基类提供了几种方法的默认实现,特征通过abstract override选择性地覆盖某些方法,从而充当可堆叠的特征/混合。

From a design perspective this works well, and maps to the domain so that I can add a filtering function from here (one trait) with a predicate from here (another trait) etc.从设计的角度来看,这很有效,并映射到域,以便我可以从这里(一个特征)添加一个过滤函数,从这里(另一个特征)添加一个谓词等。

However, now I'd like some of my traits to take implicit parameters.但是,现在我希望我的一些特征采用隐式参数。 I'm happy that this still makes sense from a design perspective, and wouldn't prove confusing in practice.我很高兴从设计的角度来看这仍然有意义,并且在实践中不会造成混淆。 However, I cannot convince the compiler to run with it.但是,我无法说服编译器使用它运行。

The core of the problem seems to be that I cannot provide constructor arguments for a trait, such that they could be marked implicit.问题的核心似乎是我无法为特征提供构造函数参数,因此它们可以被标记为隐式。 Referencing the implicit parameter within a method implementation fails to compile with the expected "could not find implicit value" message;在方法实现中引用隐式参数无法通过预期的“找不到隐式值”消息进行编译; I tried to "propagate" the implicit from construction stage (where, in practice, it's always in scope) to being available within the method via我试图将隐式从构造阶段(在实践中,它总是在范围内)“传播”到在方法中可用

implicit val e = implicitly[ClassName]

but (as no doubt many of you expect) that definition failed with the same message.但是(毫无疑问,你们中的许多人都期望)定义因相同的消息而失败。

It seems that the problem here is that I can't convince the compiler to tag the signature of the trait itself with an implicit ClassName flag, and force callers (ie those who mix the trait into an object) to provide the implicit.似乎这里的问题是我无法说服编译器使用implicit ClassName标志标记特征本身的签名,并强制调用者(即那些将特征混合到对象中的人)提供隐式。 Currently my callers are doing so, but the compiler isn't checking at this level.目前我的调用者正在这样做,但编译器没有在这个级别进行检查。


Is there any way to mark a trait as requiring certain implicits be available at construction time?有什么方法可以将特征标记为要求某些隐式在构建时可用?

(And if not, is this simply not implemented yet or is there a deeper reason why this is impractical?) (如果没有,这是否还没有实施,或者有更深层次的原因为什么这是不切实际的?)

Actually, I've wanted this quite often before, but just came up with this idea.实际上,我以前经常想要这个,但只是想出了这个主意。 You can translate你可以翻译

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

to [EDITED: original version didn't provide access to implicit for other methods] [编辑:原始版本没有提供对其他方法的隐式访问]

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
}

This isn't possible.这是不可能的。

But you can use implicitly and Scala's type inference to make this as painless as possible.但是您可以使用implicitly和 Scala 的类型推断来使这尽可能轻松。

trait MyTrait {

    protected[this] implicit def e: ClassName

}

and then接着

class MyClass extends MyTrait {

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

}

Succinct, and doesn't even require writing the type in the extending class.简洁,甚至不需要在扩展类中编写类型。

I ran into this problem a few times and indeed it's a bit annoying, but not too much.我遇到过几次这个问题,确实有点烦人,但不是太多。 Abstract members and parameters are usually two alternative ways of doing the same thing, with their advantages and disadvantages.抽象成员和参数通常是做同一件事的两种替代方式,各有优缺点。 For traits having an abstract member is not too inconvenient, because you still need another class to implement the trait.*对于具有抽象成员的特征并不太方便,因为您仍然需要另一个类来实现特征。*

Therefore, you should simply have an abstract value declaration in the trait, so that implementing classes have to supply an implicit for you.因此,您应该在 trait 中简单地声明一个抽象值,以便实现类必须为您提供一个隐式。 See the following example - which compiles correctly, and shows two ways of implementing the given trait:请参阅以下示例 - 正确编译,并显示了实现给定特征的两种方法:

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]

The basic idea I show is also present in Knut Arne Vedaa's answer, but I tried to make a more compelling and convenient example, dropping usage of unneeded features.我展示的基本思想也出现在 Knut Arne Vedaa 的回答中,但我试图制作一个更引人注目和更方便的例子,放弃使用不需要的功能。

  • This is not the reason why traits cannot accept parameters - I don't know it.这不是traits不能接受参数的原因——我不知道。 I'm just arguing that the limitation is acceptable in this case.我只是认为在这种情况下限制是可以接受的。

You could do it like this:你可以这样做:

abstract class C

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

implicit val n = 3

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

But I'm not sure if there's any point in it - you could just as well reference the implicit value explicitly.但我不确定其中是否有任何意义 - 您也可以明确引用隐式值。

I guess what you want is to get rid of the implementation of i in the instantiation, but as you say yourself, the core of the problem is that traits doesn't take constructor parameters - whether they would be implicit or not doesn't matter.我想你想要的是在实例化中摆脱i的实现,但正如你自己所说,问题的核心是特征不接受构造函数参数——它们是否是隐式的并不重要.

A possible solution for this problem would be to add a new feature to the already valid syntax:这个问题的一个可能的解决方案是向已经有效的语法添加一个新功能:

trait A {
    implicit val i: Int
}

where i would be implemented by the compiler if an implicit was in scope.如果隐式在范围内, i将由编译器实现。

As it looks like this isn't possible, I went for the option of declaring the implicit val on the base class' constructor.由于看起来这是不可能的,我选择了在基类的构造函数上声明隐式val的选项。 As pointed out in the question this isn't ideal, but it satisfies the compiler and, pragmatically, isn't too much of a burden in my particular case.正如问题中所指出的那样,这并不理想,但它满足了编译器的要求,而且在我的特定情况下,从务实的角度来看,这并没有太大的负担。

If anyone has a better solution though, I'd be happy to hear and accept it.如果有人有更好的解决方案,我很乐意听到并接受它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM