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

发布于 2024-11-28 14:40:52 字数 681 浏览 1 评论 0原文

我正在设计一个类层次结构,它由一个基类和几个特征组成。基类提供了几种方法的默认实现,特征通过抽象覆盖选择性地覆盖某些方法,从而充当可堆叠的特征/混合。

从设计角度来看,这效果很好,并且映射到域,以便我可以从这里添加过滤函数(一个特征),并使用这里的谓词(另一个特征)等。

但是,现在我希望我的一些特征采取隐式参数。我很高兴这从设计角度来看仍然有意义,并且在实践中不会造成混乱。但是,我无法说服编译器使用它运行。

问题的核心似乎是我无法为特征提供构造函数参数,以便它们可以被标记为隐式。在方法实现中引用隐式参数无法编译,并出现预期的“无法找到隐式值”消息;我试图将隐式从构造阶段(实际上,它始终在范围内)“传播”到方法中可用,但是

implicit val e = implicitly[ClassName]

(毫无疑问,正如你们中的许多人所期望的那样)定义失败了同一条消息。

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


是否有任何方法可以将特征标记为要求在构造时提供某些隐式?

(如果没有,这是否还没有实现,或者是否有更深层次的原因导致这是不切实际的?)

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.

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 (i.e. those who mix the trait into an object) to provide the implicit. 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?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

○闲身 2024-12-05 14:40:52

这是不可能的。

但是您可以隐式 和 Scala 的类型推断来使其尽可能轻松。

trait MyTrait {

    protected[this] implicit def e: ClassName

}

然后

class MyClass extends MyTrait {

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

}

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

This isn't possible.

But you can use implicitly and Scala's type inference to make this as painless as possible.

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.

青朷 2024-12-05 14:40:52

其实我以前也经常有这样的想法,但只是想出了这个主意。您可以翻译

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

为[编辑:原始版本没有提供对其他方法的隐式访问]

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
}

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
}
毁梦 2024-12-05 14:40:52

我遇到过这个问题几次,确实有点烦人,但还不算太烦人。抽象成员和参数通常是做同一件事的两种替代方法,各有优缺点。对于具有抽象成员的特征来说并不太不方便,因为你仍然需要另一个类来实现该特征。*

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

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 的答案中,但我试图制作一个更引人注目和更方便的示例,放弃使用不需要的功能。

  • 这不是特征不能接受参数的原因 - 我不知道。我只是想说,在这种情况下,这种限制是可以接受的。

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. 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.

  • This is not the reason why traits cannot accept parameters - I don't know it. I'm just arguing that the limitation is acceptable in this case.
国际总奸 2024-12-05 14:40:52

你可以这样做:

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 的实现,但正如你自己所说,问题的核心是特征不接受构造函数参数 - 无论它们是隐式与否并不重要。

这个问题的一个可能的解决方案是在已经有效的语法中添加一个新功能:

trait A {
    implicit val i: Int
}

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

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.

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.

枯寂 2024-12-05 14:40:52

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

如果有人有更好的解决方案,我很乐意听到并接受。

As it looks like this isn't possible, I went for the option of declaring the implicit val on the base class' constructor. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文