Scala 如何将特质声明为“内隐”;构造函数参数“;?

Scala 如何将特质声明为“内隐”;构造函数参数“;?,scala,implicit,traits,Scala,Implicit,Traits,我正在设计一个类层次结构,它由一个基类和几个特性组成。基类提供了几种方法的默认实现,traits通过抽象覆盖选择性地覆盖某些方法,从而充当可堆叠的traits/mixin 从设计的角度来看,这工作得很好,并且映射到域,这样我就可以从这里添加一个过滤函数(一个特征)和一个谓词(另一个特征),等等 但是,现在我想让我的一些特征采用隐式参数。我很高兴,从设计的角度来看,这仍然是有意义的,并且在实践中不会令人困惑。但是,我无法说服编译器使用它运行 问题的核心似乎是我不能为一个特性提供构造函数参数,这样它

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

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

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

问题的核心似乎是我不能为一个特性提供构造函数参数,这样它们就可以被标记为隐式。在方法实现中引用隐式参数无法编译,并显示预期的“找不到隐式值”消息;我试图从构造阶段(在实践中,它总是在范围内)将隐式方法“传播”到通过

implicit val e = implicitly[ClassName]
但是(毫无疑问,正如你们中的许多人所期待的那样),这个定义以同样的信息失败了

这里的问题似乎是,我无法说服编译器使用
隐式类名
标志标记trait本身的签名,并强制调用方(即将trait混合到对象中的调用方)提供隐式类名称。目前我的调用者正在这样做,但是编译器没有在这个级别进行检查


是否有任何方法将某个特征标记为需要在构建时提供某些隐含信息?


(如果没有,这是不是还没有实现,或者这是不可行的更深层次的原因?

事实上,我以前很想这样做,但我刚刚想到了这个想法。你可以翻译

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
}

你可以这样做:

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
将由编译器实现,如果一个隐式在作用域中。

我遇到过几次这个问题,确实有点烦人,但也不算太多。抽象成员和参数通常是做同一件事的两种可选方法,各有优缺点;对于trait来说,拥有一个抽象成员并不太麻烦,因为您无论如何都需要另一个类来实现trait*

因此,您只需要在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]
克努特·阿恩·韦达(Knut Arne Vedaa)的回答中也包含了我所展示的基本思想,但我试图给出一个更具说服力和更方便的例子,放弃使用不必要的功能


*这不是trait不能接受参数的原因——我不知道。我只是认为这种限制在这种情况下是可以接受的。

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

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

这是不可能的

但是您可以隐式地使用
和Scala的类型推断来尽可能轻松地完成这项工作

trait MyTrait {

    protected[this] implicit def e: ClassName

}
然后

class MyClass extends MyTrait {

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

}

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

这是否会使调用程序在匿名对象中显式定义
implWrap
,因为它是trait中的抽象字段?(如果不是,我不明白它是如何设置的;你介意解释一下吗?)是的,但请看注释:如果他想使用隐式,他可以只编写
val implWrap=ClassNameW
。我没有看到更好的方法:正如你在问题中提到的,traits没有任何构造函数参数(可以标记为隐式)。我做了类似的事情,但使用类型推断来帮助我。但是,通过这种方式,在
Base[t]
中定义方法时,您无法访问隐式
排序[t]
。如果将
numT
设为隐式,则可以解决此问题,但
val numT=隐式[Ordering[T]]
将成为一个无限循环。如果修改
Base
以解决此问题,则无法编写Der1,但Der2仍然可以工作,并且在等效的情况下仍然比Der1更紧凑<代码>特征基[T]{implicit val numT:ORDINGER[T]}类Der2[T](implicit val numT:ORDINGER[T])扩展基[T]
Scala 2.13:
找不到参数e的隐式值