拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 在Scala中使用PureConfig的泛型型别

在Scala中使用PureConfig的泛型型别

白鹭 - 2022-03-25 2218 0 0

我正在尝试从具有泛型型别的方法中呼叫 PureConfig 的 loadOrThrow:

def load[T: ClassTag](path: String): T = {
    import pureconfig.generic.auto._
    ConfigSource.file(path).loadOrThrow[T]
}

当我尝试从主类呼叫它时,出现以下错误:

could not find implicit value for parameter reader: pureconfig.ConfigReader[T]
    ConfigSource.file(path).loadOrThrow[T]

我可以import pureconfig.generic.auto._没有主课的情况下解决这个问题吗

uj5u.com热心网友回复:

总结评论并解释这个编译码器是如何作业的。

当你这样做时:

def something[T: ConfigReader] = ...

你正在使用语法糖

// Scala 2
def something[T](implicit configReader: ConfigReader[T]) = ...

// Scala 3
def something[T](using configReader: ConfigReader[T]) = ...

在您写信时的呼叫站点上:

something[T]

编译器实际上是

something(configReaderForT /* : ConfigReader[T] */)

所以基本上它是编译器支持的基于型别的依赖注入。并且依赖注入必须从某个地方获取要传递的值。

编译器如何获取该以传递它?它必须在范围内按其型别找到它。应该有一个def标记为implicit(Scala 2) 或given(Scala 3)的这种型别的明确最接近的值(或回传此值)。

// Scala 2
implicit val fooConfigReader: ConfigReader[Foo] = ...
something[Foo] // will use fooConfigReader

// Scala 3
given fooConfigReader: ConfigReader[Foo] = ...
something[Foo] // will use fooConfigReader

您必须自己定义此值/方法或汇入它 - 在需要它的范围内 - 否则编译器只会尝试查看对您的型别有贡献的所有型别的伴随物件T- 如果T是特定的。(或者如果在编译器错误讯息中找不到它,则失败)。

// Example of companion object approach
// This is type Foo
case class Foo()
// This is Foo's companion object
object Foo {
  // This approach (calling derivation manually) is called semiauto
  // and it usually needs a separate import
  import pureconfig.generic.semiauto._

  implicit val configReader: ConfigReader[Foo] = deriveReader[Foo]
}

// By requiring ConfigReader[Foo] (if it wasn't defined/imported
// into the scope that needs it) compiler would look into:
// * ConfigReader companion object
// * Foo companion object
// ConfigReader doesn't have such instance but Foo does.

如果T是通用的,那么您必须将该implicit/given作为自变量传递- 但是您只是推迟必须指定它并让编译器找到/生成它的时刻。

// Tells compiler to use value passed as parameter
// as it wouldn't be able to generate it based on generic information

// implicit/using expressed as "type bounds" (valid in Scala 2 and 3)
def something[T: ConfigReader] = ...
// Scala 2
def something[T](implicit configReader: ConfigReader[T]) = ...
// Scala 3
def something[T](using configReader: ConfigReader[T]) = ...

// It works the same for class constructors.

在 PureConfig 的情况下,pureconfig.generic.autocontains implicit defs 为指定的 生成值T如果要生成它,则必须将其汇入到需要该特定实体的位置。您可以在伴随物件中执行此操作,以使其可在此ConfigReader特定型别需要的任何地方自动汇入,或将其汇入 main(或将 T 指定为某物的任何其他位置)。一种方式或其他,你将必须从某个地方获得,然后添加此[T: ConfigReader](implicit configReader: ConfigReader[T])在所有方法签名这不应该硬编码T到任何东西。

总结您的选择是:

  • 将汇入保留在主目录中(如果您在主目录中硬编码T为特定型别)
  • 派生它并定义为implicit其他地方,然后从那里汇入它(有些人在traits 中然后混合它们,但我不喜欢这种方法)
  • 派生它并implicit在伴随物件中定义为

只要您希望您的配置被决议值而不是无型别 JSON (HOCON) 而无需自己撰写这些编译码器,您就必须在某处执行该自动(或半自动)派生。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *