如何为 Product 和 Record 创建重载?
How to create overloads for Product and Record?
由于它们都是 HList 类型,因此我必须创建具有不同名称的函数。这是 kittens
中的示例
//sequence a product HList
trait Sequencer[L <: HList] {
type Out
def apply(in: L): Out
}
//sequence an extensible record HList
trait RecordSequencer[L <: HList] {
type Out
def apply(in: L): Out
}
implicit class sequenceOps[L <: HList](self: L) {
def sequence(implicit seq: Sequencer[L]): seq.Out = seq(self)
def sequenceRecord(implicit seq: RecordSequencer[L]): seq.Out = seq(self) //version for extensible records
}
注意这里我必须使用名称 sequenceRecord 来区别于产品 HList 的序列方法。我发现它非常随意和繁琐。
有没有办法让可扩展记录和产品 HList 具有同名的序列方法?或者你总是必须为这两种HList提供两个不同的名称?
您可以使用 L
的 shapeless.ops.record.Keys
实例的存在来区分记录和 non-record HLists
。然后剩下的工作就是兼顾隐式优先级以确保这两种扩展方法不会冲突。像下面这样的东西就可以完成这项工作,
import shapeless._, ops.record._, syntax.singleton._
//sequence a product HList
trait Sequencer[L <: HList] {
type Out
def apply(in: L): Out
}
object Sequencer {
implicit def mkSequencer[L <: HList]:
Sequencer[L] { type Out = String } =
new Sequencer[L] {
type Out = String
def apply(l: L): String = "Sequencer"
}
}
//sequence an extensible record HList
trait RecordSequencer[L <: HList] {
type Out
def apply(in: L): Out
}
object RecordSequencer {
implicit def mkRecordSequencer[L <: HList]:
RecordSequencer[L] { type Out = String } =
new RecordSequencer[L] {
type Out = String
def apply(l: L): String = "RecordSequencer"
}
}
// Syntax for non-records
class SOps[L <: HList](self: L) {
def sequence(implicit seq: Sequencer[L]): seq.Out = seq(self)
}
// Syntax for records
class RSOps[L <: HList](self: L) {
def sequence(implicit seq: RecordSequencer[L]): seq.Out = seq(self)
}
object sequence extends lpSequence {
// Keys instance only available for records
implicit def mkRSOps[L <: HList](l: L)
(implicit keys: Keys[L]): RSOps[L] = new RSOps(l)
}
trait lpSequence {
// fallback for non-records
implicit def mkSOps[L <: HList](l: L): SOps[L] = new SOps(l)
}
object Demo extends App {
import sequence._
val l = 23 :: "foo" :: true :: HNil
val r = 'a ->> 23 :: 'b ->> "foo" :: 'c ->> true :: HNil
assert(l.sequence == "Sequencer")
assert(r.sequence == "RecordSequencer")
}
由于它们都是 HList 类型,因此我必须创建具有不同名称的函数。这是 kittens
中的示例 //sequence a product HList
trait Sequencer[L <: HList] {
type Out
def apply(in: L): Out
}
//sequence an extensible record HList
trait RecordSequencer[L <: HList] {
type Out
def apply(in: L): Out
}
implicit class sequenceOps[L <: HList](self: L) {
def sequence(implicit seq: Sequencer[L]): seq.Out = seq(self)
def sequenceRecord(implicit seq: RecordSequencer[L]): seq.Out = seq(self) //version for extensible records
}
注意这里我必须使用名称 sequenceRecord 来区别于产品 HList 的序列方法。我发现它非常随意和繁琐。 有没有办法让可扩展记录和产品 HList 具有同名的序列方法?或者你总是必须为这两种HList提供两个不同的名称?
您可以使用 L
的 shapeless.ops.record.Keys
实例的存在来区分记录和 non-record HLists
。然后剩下的工作就是兼顾隐式优先级以确保这两种扩展方法不会冲突。像下面这样的东西就可以完成这项工作,
import shapeless._, ops.record._, syntax.singleton._
//sequence a product HList
trait Sequencer[L <: HList] {
type Out
def apply(in: L): Out
}
object Sequencer {
implicit def mkSequencer[L <: HList]:
Sequencer[L] { type Out = String } =
new Sequencer[L] {
type Out = String
def apply(l: L): String = "Sequencer"
}
}
//sequence an extensible record HList
trait RecordSequencer[L <: HList] {
type Out
def apply(in: L): Out
}
object RecordSequencer {
implicit def mkRecordSequencer[L <: HList]:
RecordSequencer[L] { type Out = String } =
new RecordSequencer[L] {
type Out = String
def apply(l: L): String = "RecordSequencer"
}
}
// Syntax for non-records
class SOps[L <: HList](self: L) {
def sequence(implicit seq: Sequencer[L]): seq.Out = seq(self)
}
// Syntax for records
class RSOps[L <: HList](self: L) {
def sequence(implicit seq: RecordSequencer[L]): seq.Out = seq(self)
}
object sequence extends lpSequence {
// Keys instance only available for records
implicit def mkRSOps[L <: HList](l: L)
(implicit keys: Keys[L]): RSOps[L] = new RSOps(l)
}
trait lpSequence {
// fallback for non-records
implicit def mkSOps[L <: HList](l: L): SOps[L] = new SOps(l)
}
object Demo extends App {
import sequence._
val l = 23 :: "foo" :: true :: HNil
val r = 'a ->> 23 :: 'b ->> "foo" :: 'c ->> true :: HNil
assert(l.sequence == "Sequencer")
assert(r.sequence == "RecordSequencer")
}