可扩展的记录类型
Extensible record types
我正在尝试使用 shapeless 的可扩展记录进行简单练习。
这是一个名为 Projection
的类型类,它应该能够在某种程度上结合 Updater
和 Remover
的功能:
import shapeless._
import shapeless.tag._
import shapeless.record._
import shapeless.labelled._
import shapeless.ops.record._
import shapeless.syntax._
// Probably way too many imports
trait Projection[A <: HList, K, V] {
type B <: HList
def to(a: A, v: V): B
def from(b: B): A
}
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type Key[K] = Symbol with Tagged[K]
type F[K, V] = V with FieldType[Key[K], V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[K],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, K, (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[Key[K]](v)
}
}
我比较简单的测试
import Projection._
val thirdFieldWitness = Witness("thirdField")
val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
不幸的是失败并出现错误
could not find implicit value for parameter e: Projection[shapeless.HNil,ProjectionSpec.this.thirdFieldWitness.T,Boolean]
[error] val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
-Xlog-implicits
说明原因:
ProjectionSpec.scala:18:35: record.this.Remover.mkRemover is not a valid implicit value for shapeless.ops.record.Remover.Aux[Boolean with shapeless.labelled.FieldType[Projection.Key[ProjectionSpec.this.thirdFieldWitness.T],Boolean] :: shapeless.HNil,ProjectionSpec.this.thirdFieldWitness.T,(Boolean, shapeless.HNil)] because:
[info] hasMatchingSymbol reported error: No field String("thirdField") in record type Boolean with shapeless.labelled.FieldType[Projection.Key[ProjectionSpec.this.thirdFieldWitness.T],Boolean] :: shapeless.HNil
[info] val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
请帮助我理解此消息并告诉我如何修复它。
是否有更简单的方法来对标记的仿制药进行这种扩展和缩短?
除-Xlog-implicits
外,一种更标准的隐式调试方法是手动解析它们并查看编译错误。
尝试
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type Key[K] = Symbol with Tagged[K]
type F[K, V] = FieldType[Key[K], V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[Key[K]],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, Key[K], (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[Key[K]](v)
}
}
然后
implicitly[Projection.Aux[HNil, "thirdField", Boolean, Record.`'thirdField -> Boolean`.T]]
编译。
但是虽然implicitly[thirdFieldWitness.T =:= "thirdField"]
implicitly[Projection.Aux[HNil, thirdFieldWitness.T, Boolean, Record.`'thirdField -> Boolean`.T]]
仍然无法编译。但是手动解决了
implicitly[Projection.Aux[HNil,
thirdFieldWitness.T,
Boolean,
Record.`'thirdField -> Boolean`.T
]](Projection.mkProjection(
implicitly[Witness.Aux[Witness.`'thirdField`.T]],
implicitly[Updater.Aux[HNil, FieldType[Witness.`'thirdField`.T, Boolean], Record.`'thirdField -> Boolean`.T]],
implicitly[Remover.Aux[Record.`'thirdField -> Boolean`.T, Witness.`'thirdField`.T, (Boolean, HNil)]]
))
编译。事情似乎是 implicitly[Witness.Aux[Key["thirdField"]]]
编译但 implicitly[Witness.Aux[Key[thirdFieldWitness.T]]]
没有(“Symbol with Tagged[thirdFieldWitness.T]
不是单例类型”)。
如果添加
可以修复编译
implicit def extraWitness[S <: String](implicit
w: Witness.Aux[S]
): Witness.Aux[Symbol @@ S] = Witness.mkWitness(tag[S](Symbol(w.value)))
我会使用标准的基于符号的 API
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type F[K, V] = FieldType[K, V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[K],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, K, (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[K](v)
}
}
implicitly[Projection.Aux[HNil, Witness.`'thirdField`.T, Boolean, Record.`'thirdField -> Boolean`.T]]
val thirdFieldWitness = Witness('thirdField)
implicitly[Projection.Aux[HNil, thirdFieldWitness.T, Boolean, Record.`'thirdField -> Boolean`.T]]
我正在尝试使用 shapeless 的可扩展记录进行简单练习。
这是一个名为 Projection
的类型类,它应该能够在某种程度上结合 Updater
和 Remover
的功能:
import shapeless._
import shapeless.tag._
import shapeless.record._
import shapeless.labelled._
import shapeless.ops.record._
import shapeless.syntax._
// Probably way too many imports
trait Projection[A <: HList, K, V] {
type B <: HList
def to(a: A, v: V): B
def from(b: B): A
}
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type Key[K] = Symbol with Tagged[K]
type F[K, V] = V with FieldType[Key[K], V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[K],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, K, (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[Key[K]](v)
}
}
我比较简单的测试
import Projection._
val thirdFieldWitness = Witness("thirdField")
val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
不幸的是失败并出现错误
could not find implicit value for parameter e: Projection[shapeless.HNil,ProjectionSpec.this.thirdFieldWitness.T,Boolean]
[error] val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
-Xlog-implicits
说明原因:
ProjectionSpec.scala:18:35: record.this.Remover.mkRemover is not a valid implicit value for shapeless.ops.record.Remover.Aux[Boolean with shapeless.labelled.FieldType[Projection.Key[ProjectionSpec.this.thirdFieldWitness.T],Boolean] :: shapeless.HNil,ProjectionSpec.this.thirdFieldWitness.T,(Boolean, shapeless.HNil)] because:
[info] hasMatchingSymbol reported error: No field String("thirdField") in record type Boolean with shapeless.labelled.FieldType[Projection.Key[ProjectionSpec.this.thirdFieldWitness.T],Boolean] :: shapeless.HNil
[info] val projector = implicitly[Projection[HNil, thirdFieldWitness.T, Boolean]]
请帮助我理解此消息并告诉我如何修复它。
是否有更简单的方法来对标记的仿制药进行这种扩展和缩短?
除-Xlog-implicits
外,一种更标准的隐式调试方法是手动解析它们并查看编译错误。
尝试
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type Key[K] = Symbol with Tagged[K]
type F[K, V] = FieldType[Key[K], V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[Key[K]],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, Key[K], (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[Key[K]](v)
}
}
然后
implicitly[Projection.Aux[HNil, "thirdField", Boolean, Record.`'thirdField -> Boolean`.T]]
编译。
但是虽然implicitly[thirdFieldWitness.T =:= "thirdField"]
implicitly[Projection.Aux[HNil, thirdFieldWitness.T, Boolean, Record.`'thirdField -> Boolean`.T]]
仍然无法编译。但是手动解决了
implicitly[Projection.Aux[HNil,
thirdFieldWitness.T,
Boolean,
Record.`'thirdField -> Boolean`.T
]](Projection.mkProjection(
implicitly[Witness.Aux[Witness.`'thirdField`.T]],
implicitly[Updater.Aux[HNil, FieldType[Witness.`'thirdField`.T, Boolean], Record.`'thirdField -> Boolean`.T]],
implicitly[Remover.Aux[Record.`'thirdField -> Boolean`.T, Witness.`'thirdField`.T, (Boolean, HNil)]]
))
编译。事情似乎是 implicitly[Witness.Aux[Key["thirdField"]]]
编译但 implicitly[Witness.Aux[Key[thirdFieldWitness.T]]]
没有(“Symbol with Tagged[thirdFieldWitness.T]
不是单例类型”)。
如果添加
可以修复编译implicit def extraWitness[S <: String](implicit
w: Witness.Aux[S]
): Witness.Aux[Symbol @@ S] = Witness.mkWitness(tag[S](Symbol(w.value)))
我会使用标准的基于符号的 API
object Projection {
type Aux[A <: HList, K, V, B0 <: HList] = Projection[A, K, V] { type B = B0 }
type F[K, V] = FieldType[K, V]
implicit def mkProjection[A <: HList, K, V, B0 <: HList](implicit
keyWitness: Witness.Aux[K],
updater: Updater.Aux[A, F[K, V], B0],
remover: Remover.Aux[B0, K, (V, A)]
): Projection.Aux[A, K, V, B0] = new Projection[A, K, V] {
type B = B0
def from(b: B0): A = b - keyWitness
def to(a: A, v: V): B0 = a + field[K](v)
}
}
implicitly[Projection.Aux[HNil, Witness.`'thirdField`.T, Boolean, Record.`'thirdField -> Boolean`.T]]
val thirdFieldWitness = Witness('thirdField)
implicitly[Projection.Aux[HNil, thirdFieldWitness.T, Boolean, Record.`'thirdField -> Boolean`.T]]