Scala:缺少隐式泛型参数 shapeless.LabelledGeneric.Aux[T,L],如何提供?
Scala: missing implicit generic parameter shapeless.LabelledGeneric.Aux[T,L], how to provide it?
我正在编写与 finagle-postgres 一起使用 DB 的代码,我有映射器,并且确定我想尽可能多地将代码的公共部分移动到通用映射器。
所以,例如,我有 Mapper
case class ProcessState(
...
)
class ProcessStateMapper(client: PostgresClient)
extends EntityMapper[ProcessState]("process_state", client)(rowDecoder) {
...
def create(state: ProcessState): Future[ProcessState] = {
val fields = Updates(state)
val columnNames = fields.updates.map(_._1).mkString(", ")
val placeholders = (1 to fields.updates.size).map(i => s"$$$i").mkString(", ")
for {
inserted <- client.prepareAndExecute(
s"INSERT INTO $tableName ($columnNames) VALUES ($placeholders)", fields.params: _*)
} yield {
require(inserted > 0, s"Failed to create $tableName: $state")
state
}
}
}
此创建方法工作正常,Updates
需要隐式 shapeless.LabelledGeneric.Aux[T,L]
,并且在此上下文中找到它。
但是如果我将方法移至泛型 class,由于类型擦除,它无法找到隐式值...
abstract class EntityMapper[T <: Product](
val tableName: String, val client: PostgresClient)(
implicit val rowDecoder: RowDecoder[T]) {
...
def create(state: T): Future[T] = {
val fields = Updates(state)
val columnNames = fields.updates.map(_._1).mkString(", ")
val placeholders = (1 to fields.updates.size).map(i => s"$$$i").mkString(", ")
for {
inserted <- client.prepareAndExecute(
s"INSERT INTO $tableName ($columnNames) VALUES ($placeholders)", fields.params: _*)
} yield {
require(inserted > 0, s"Failed to create $tableName: $state")
state
}
}
}
所以这段代码没有编译错误
could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[T,L]
val fields = Updates(state)
所以我尝试提供这个参数
abstract class EntityMapper[T <: Product](
val tableName: String, val client: PostgresClient)(
implicit val rowDecoder: RowDecoder[T], val lgen: LabelledGeneric.Aux[T, _]) {
...
}
class ProcessStateMapper(client: PostgresClient)
extends EntityMapper[ProcessState]("process_state", client)(rowDecoder, ProcessStateMapper.lgen) {
...
}
object ProcessStateMapper {
...
val lgen: LabelledGeneric.Aux[ProcessState, _] = LabelledGeneric[ProcessState]
}
但是没有用,我尝试了一些其他的提供方式,但是都失败了。你知道这样做的正确方法是什么吗?
Updates#apply
需要的不仅仅是 LabelledGeneric.Aux[P, L]
。
完整签名为:
def apply[P <: Product, L <: HList, MP <: HList](p: P)(implicit
gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer
): Updates
为了能够调用该函数,您还需要将所有这些隐式参数添加到您的 class 中。由于 L
和 MP
类型参数也需要在范围内,因此您还需要将它们添加为 class 的类型参数。所以你最终得到这个:
abstract class EntityMapper[P <: Product, L <: HList, MP <: HList](
val tableName: String, val client: PostgresClient)(
implicit rowDecoder: RowDecoder[T],
gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer) {
这太臭了!不仅因为所有这些隐式,还因为现在 class.
中有两个完全没有意义的类型参数
但是您可以使用一些隐含的方法来解决这个问题:
trait Updatable[P <: Product] {
def apply(p: P): Updates
}
object Updatable {
implicit def instance[P <: Product, L <: HList, MP <: HList](
implicit gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer): Updatable[P] = new Updatable[P] {
def apply(p: P): Updates = Updates(p)
}
}
现在您可以这样写 EntityMapper
:
abstract class EntityMapper[T <: Product](
val tableName: String,
val client: PostgresClient)(
implicit updatable: Updatable[T]) {
…
def create(state: T): Future[T] = {
val fields = updatable(state)
…
}
…
}
我正在编写与 finagle-postgres 一起使用 DB 的代码,我有映射器,并且确定我想尽可能多地将代码的公共部分移动到通用映射器。
所以,例如,我有 Mapper
case class ProcessState(
...
)
class ProcessStateMapper(client: PostgresClient)
extends EntityMapper[ProcessState]("process_state", client)(rowDecoder) {
...
def create(state: ProcessState): Future[ProcessState] = {
val fields = Updates(state)
val columnNames = fields.updates.map(_._1).mkString(", ")
val placeholders = (1 to fields.updates.size).map(i => s"$$$i").mkString(", ")
for {
inserted <- client.prepareAndExecute(
s"INSERT INTO $tableName ($columnNames) VALUES ($placeholders)", fields.params: _*)
} yield {
require(inserted > 0, s"Failed to create $tableName: $state")
state
}
}
}
此创建方法工作正常,Updates
需要隐式 shapeless.LabelledGeneric.Aux[T,L]
,并且在此上下文中找到它。
但是如果我将方法移至泛型 class,由于类型擦除,它无法找到隐式值...
abstract class EntityMapper[T <: Product](
val tableName: String, val client: PostgresClient)(
implicit val rowDecoder: RowDecoder[T]) {
...
def create(state: T): Future[T] = {
val fields = Updates(state)
val columnNames = fields.updates.map(_._1).mkString(", ")
val placeholders = (1 to fields.updates.size).map(i => s"$$$i").mkString(", ")
for {
inserted <- client.prepareAndExecute(
s"INSERT INTO $tableName ($columnNames) VALUES ($placeholders)", fields.params: _*)
} yield {
require(inserted > 0, s"Failed to create $tableName: $state")
state
}
}
}
所以这段代码没有编译错误
could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[T,L]
val fields = Updates(state)
所以我尝试提供这个参数
abstract class EntityMapper[T <: Product](
val tableName: String, val client: PostgresClient)(
implicit val rowDecoder: RowDecoder[T], val lgen: LabelledGeneric.Aux[T, _]) {
...
}
class ProcessStateMapper(client: PostgresClient)
extends EntityMapper[ProcessState]("process_state", client)(rowDecoder, ProcessStateMapper.lgen) {
...
}
object ProcessStateMapper {
...
val lgen: LabelledGeneric.Aux[ProcessState, _] = LabelledGeneric[ProcessState]
}
但是没有用,我尝试了一些其他的提供方式,但是都失败了。你知道这样做的正确方法是什么吗?
Updates#apply
需要的不仅仅是 LabelledGeneric.Aux[P, L]
。
完整签名为:
def apply[P <: Product, L <: HList, MP <: HList](p: P)(implicit
gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer
): Updates
为了能够调用该函数,您还需要将所有这些隐式参数添加到您的 class 中。由于 L
和 MP
类型参数也需要在范围内,因此您还需要将它们添加为 class 的类型参数。所以你最终得到这个:
abstract class EntityMapper[P <: Product, L <: HList, MP <: HList](
val tableName: String, val client: PostgresClient)(
implicit rowDecoder: RowDecoder[T],
gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer) {
这太臭了!不仅因为所有这些隐式,还因为现在 class.
中有两个完全没有意义的类型参数但是您可以使用一些隐含的方法来解决这个问题:
trait Updatable[P <: Product] {
def apply(p: P): Updates
}
object Updatable {
implicit def instance[P <: Product, L <: HList, MP <: HList](
implicit gen: LabelledGeneric.Aux[P, L],
mapper: Mapper.Aux[toLabelledParam.type, L, MP],
toList: ToList[MP, (String, Param[_])],
columnNamer: ColumnNamer): Updatable[P] = new Updatable[P] {
def apply(p: P): Updates = Updates(p)
}
}
现在您可以这样写 EntityMapper
:
abstract class EntityMapper[T <: Product](
val tableName: String,
val client: PostgresClient)(
implicit updatable: Updatable[T]) {
…
def create(state: T): Future[T] = {
val fields = updatable(state)
…
}
…
}