Scala:缺少shapeless隐式泛型参数.LabelledGeneric.Ax[T,L],如何提供



我正在编写使用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],在这个上下文中它找到了它

但如果我把方法移到泛型类,由于类型擦除,它找不到隐式值。。。

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

为了能够调用该函数,还需要将所有这些隐式参数添加到类中。由于LMP类型参数也需要在作用域中,因此还需要将它们添加为类的类型参数。所以你最终得到了这个:

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) {

这太臭了!这不仅是因为所有这些隐式,还因为现在类中有两个完全没有意义的类型参数。

但你可以用一些隐含的方法来解决这个问题:

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)
…
}
…
}

最新更新