Scala 使用无形状的通用默认实例为案例类构建,并使用定义公共createValue方法的参数
我正在努力实现以下目标——使用shapeless似乎是一条不错的途径 考虑到当前的类模型:Scala 使用无形状的通用默认实例为案例类构建,并使用定义公共createValue方法的参数,scala,shapeless,path-dependent-type,singleton-type,Scala,Shapeless,Path Dependent Type,Singleton Type,我正在努力实现以下目标——使用shapeless似乎是一条不错的途径 考虑到当前的类模型: import shapeless._ object ShapelessTest { case class Definition[T](id: String) extends Typeable[T] { type V = Value[T] override def cast(t: Any): Option[T] = createValue(t.asInstanceOf[Option[
import shapeless._
object ShapelessTest {
case class Definition[T](id: String) extends Typeable[T] {
type V = Value[T]
override def cast(t: Any): Option[T] = createValue(t.asInstanceOf[Option[T]]).value
override def describe: String = s"$id"
def createValue(value: Option[T]): V =
Value[T](this, value)
}
case class Value[T](definition: Definition[T], value: Option[T])
val DefA: Definition[Int] = Definition[Int]("defA")
val DefB: Definition[String] = Definition[String]("defB")
case class Instance(valA: DefA.V,
valB: DefB.V)
def main(args: Array[String]): Unit = {
val Empty: Instance = Instance(
DefA.createValue(None),
DefB.createValue(None)
)
println(s"Empty manual: $Empty")
val emptyHl = Generic[Instance].from(DefA.createValue(None) :: DefB.createValue(None) :: HNil)
println(s"Empty hlist: $emptyHl")
}
}
我可以创建一个空实例作为空实例,方法是手动调用正确定义上的createValue方法,或者使用shapeless转换HList
我试图弄清楚是否有可能以编程方式为每个具有Value类型字段的类创建一个空实例
换句话说,我希望能够调用
val Empty: Instance = empty[Instance]
并具有与emptyHl或Empty实例相同的结果
这似乎类似于shapeless指南中的8.3随机值生成器示例,但我没有生成随机数,而是为case类中的每个类型使用函数,而是尝试具体化每个参数的具体定义类型,并对其调用createValueNone方法
我一直在努力,但没有成功
使用在TypeTable上定义了Poly1的hlist.Mapper,我可以获得参数列表,但无法调用TypeTable上的任何方法
任何帮助都将不胜感激,谢谢!:
4月9日更新
我能想出一个非常复杂的解决方案——不幸的是,我做了很多铸造工作,但效果很好
我想重复一下,让它更好。我尝试使用natMapper:NatTRel,但无法在单例类型上使用它。
我相信这可以做得更好!欢迎提出任何建议
导入shapeless.ops.hlist
导入shapeless.ops.hlist.{Comapped,Reify}
导入无形状。{Generic,HList,HNil}
对象无形状测试2{
案例类定义[T]id:字符串{
类型V=值[this.type]
def createValuevalue:选项[T]=
新值[此.type]{
类型NT=T
覆盖val valueT:选项[T]=值
覆盖val attrDef:Definition.this.type=Definition.this
}
}
特征值[D]{
NT型
val attrDef:D
val valueT:选项[NT]
}
对象定义扩展定义[Int]DefA
对象DefB扩展定义[Int]DefB
对象DefC扩展定义[String]DefC
案例类InstancevalA:DefA.V,
valB:DefB.V,
valC:DefC.V
//编译安全
val Inst1:Instance=Instance
DefA.createValueSome1,
DefB.createValueSome2,
DefC.createValueSome2
def mainargs:数组[字符串]:单位={
def empty[A如果不使用Typeable,您想做的事情会容易得多。顺便说一句,Typeable[T]应该用作类型类,而不是混入。一般来说,派生无形状类型类的模式如下所示:
import shapeless._
trait Empty[A] {
def createValue(): A
}
object Empty {
// summoning method
def apply[T](implicit empty: Empty[T]): T = empty.createValue()
// implicits for HNil and HCons build HList if we provide the right implicits
implicit val emptyHNil: Empty[HNil] = () => HNil
implicit def emptyHCons[H, T](
implicit
head: Empty[H],
tail: Empty[T]
): Empty[H :: T] = () => head.createValue() :: tail.createValue()
// Generic will translate it HList into Product type
implicit def emptyProduct[T, Rep](
implicit
gen: Generic.Aux[T, Rep],
empty: Empty[Rep]
): Empty[T] = () => gen.from(empty.createValue())
// if you need default instances accessible everywhere without import put them here
}
用作
case class Instance(int: Int, string: String)
implicit int: Empty[Int] = () => 0
implicit int: Empty[String] = () => ""
Empty[Instance].createValue[Instance] // Instance(0, "")
在您的情况下,您必须使值[T]在隐式作用域中可用,并派生值[Instance]如何在隐式范围内提供基本值实例取决于您:您可以将它们放入伴随对象中,或者手动定义,或者从隐式范围内已有的其他类型派生
长话短说,您想要的应该是可能的,但您必须首先将事情放入隐式范围。您想要做的事情可以在根本不使用Typeable的情况下更容易实现,顺便说一句,Typeable[T]应该用作一个类型类,而不是混入。一般来说,派生无形状类型类的模式如下所示:
import shapeless._
trait Empty[A] {
def createValue(): A
}
object Empty {
// summoning method
def apply[T](implicit empty: Empty[T]): T = empty.createValue()
// implicits for HNil and HCons build HList if we provide the right implicits
implicit val emptyHNil: Empty[HNil] = () => HNil
implicit def emptyHCons[H, T](
implicit
head: Empty[H],
tail: Empty[T]
): Empty[H :: T] = () => head.createValue() :: tail.createValue()
// Generic will translate it HList into Product type
implicit def emptyProduct[T, Rep](
implicit
gen: Generic.Aux[T, Rep],
empty: Empty[Rep]
): Empty[T] = () => gen.from(empty.createValue())
// if you need default instances accessible everywhere without import put them here
}
用作
case class Instance(int: Int, string: String)
implicit int: Empty[Int] = () => 0
implicit int: Empty[String] = () => ""
Empty[Instance].createValue[Instance] // Instance(0, "")
在您的情况下,您必须使值[T]在隐式作用域中可用,并派生值[Instance]如何在隐式范围内提供基本值实例取决于您:您可以将它们放入伴随对象中,或者手动定义,或者从隐式范围内已有的其他类型派生
长话短说,你想要的应该是可能的,但你必须首先把事情放入隐式范围。我想你可能有点滥用了Typeable。使用Typeable的想法是让类型安全转换。但是你回到了一个instanceof
Typeable是一个类型类。所以你应该使用你的定义作为一个类型类。使DefA,DefB,…隐式
implicit val DefA: Definition[Int] = Definition[Int]("defA")
implicit val DefB: Definition[String] = Definition[String]("defB")
def empty[A <: Product] = new PartiallyApplied[A]
class PartiallyApplied[A <: Product] {
def apply[Vs <: HList, L <: HList, Ds <: HList]()(implicit
gen: Generic.Aux[A, Vs],
comapped: Comapped.Aux[Vs, Value, L],
liftAll: LiftAll.Aux[Definition, L, Ds],
natMapper: NatTRel[Ds, Definition, Vs, Value],
): A = {
object createValueNatTransform extends (Definition ~> Value) {
override def apply[T](definition: Definition[T]): Value[T] =
definition.createValue(None)
}
gen.from(natMapper.map(createValueNatTransform, liftAll.instances))
}
}
val Empty: Instance = empty[Instance]()
// Instance(Value(Typeable[defA],None),Value(Typeable[defB],None))
关于您的新代码,请使值协变,并对正确定义的多边形使用映射器,而不是NatTRel或运行时递归
trait Value[+D] {
type NT
val attrDef: D
val valueT: Option[NT]
}
object createValuePoly extends Poly1 {
implicit def cse[D <: Definition[T] with Singleton, T](implicit
ev: D <:< Definition[T]): Case.Aux[D, Value[D]] = at(_.createValue(None))
}
def empty[A <: Product] = new PartiallyApplied[A]
class PartiallyApplied[A <: Product] {
def apply[
V <: HList,
DL <: HList,
]()(
implicit
gen: Generic.Aux[A, V],
comapped: Comapped.Aux[V, Value, DL],
reify: Reify.Aux[DL, DL],
mapper: Mapper.Aux[createValuePoly.type, DL, V]
): A = gen.from(mapper(reify()))
}
val emptyInstance = empty[Instance]()
println(s"Empty valA: ${emptyInstance.valA.attrDef} - ${emptyInstance.valA.valueT}")
//Empty valA: Definition(defA) - None
println(s"Empty valB: ${emptyInstance.valB.attrDef} - ${emptyInstance.valB.valueT}")
//Empty valB: Definition(defB) - None
println(s"Empty valC: ${emptyInstance.valC.attrDef} - ${emptyInstance.valC.valueT}")
//Empty valC: Definition(defC) - None
我猜你是在某种程度上滥用了TypeTable。使用TypeTable的想法是让类型安全转换。但是你又回到了一种替代
Typeable是一个类型类。所以你应该使用你的定义作为一个类型类。使DefA,DefB,…隐式
implicit val DefA: Definition[Int] = Definition[Int]("defA")
implicit val DefB: Definition[String] = Definition[String]("defB")
def empty[A <: Product] = new PartiallyApplied[A]
class PartiallyApplied[A <: Product] {
def apply[Vs <: HList, L <: HList, Ds <: HList]()(implicit
gen: Generic.Aux[A, Vs],
comapped: Comapped.Aux[Vs, Value, L],
liftAll: LiftAll.Aux[Definition, L, Ds],
natMapper: NatTRel[Ds, Definition, Vs, Value],
): A = {
object createValueNatTransform extends (Definition ~> Value) {
override def apply[T](definition: Definition[T]): Value[T] =
definition.createValue(None)
}
gen.from(natMapper.map(createValueNatTransform, liftAll.instances))
}
}
val Empty: Instance = empty[Instance]()
// Instance(Value(Typeable[defA],None),Value(Typeable[defB],None))
关于您的新代码,请使值协变,并对正确定义的多边形使用映射器,而不是NatTRel或运行时递归
trait Value[+D] {
type NT
val attrDef: D
val valueT: Option[NT]
}
object createValuePoly extends Poly1 {
implicit def cse[D <: Definition[T] with Singleton, T](implicit
ev: D <:< Definition[T]): Case.Aux[D, Value[D]] = at(_.createValue(None))
}
def empty[A <: Product] = new PartiallyApplied[A]
class PartiallyApplied[A <: Product] {
def apply[
V <: HList,
DL <: HList,
]()(
implicit
gen: Generic.Aux[A, V],
comapped: Comapped.Aux[V, Value, DL],
reify: Reify.Aux[DL, DL],
mapper: Mapper.Aux[createValuePoly.type, DL, V]
): A = gen.from(mapper(reify()))
}
val emptyInstance = empty[Instance]()
println(s"Empty valA: ${emptyInstance.valA.attrDef} - ${emptyInstance.valA.valueT}")
//Empty valA: Definition(defA) - None
println(s"Empty valB: ${emptyInstance.valB.attrDef} - ${emptyInstance.valB.valueT}")
//Empty valB: Definition(defB) - None
println(s"Empty valC: ${emptyInstance.valC.attrDef} - ${emptyInstance.valC.valueT}")
//Empty valC: Definition(defC) - None
嘿,Mateusz,我仍在试图了解shapeless及其大多数概念,因此感谢您指出Typeable的用法!我尝试了您建议的方法,这与shapeless guid上用于随机生成器的方法非常相似
e、 但是它没有考虑到我的空值生成器必须基于定义类型上存在的方法。上面Dmytro建议的多边形方法似乎很有希望,但我仍然有一些问题。谢谢嘿,Mateusz,我仍然在试图把我的头绕到Shapess和它的大多数概念上,所以感谢你指出Typeable的用法!我尝试了您建议的方法,这与shapeless指南中用于random generator的方法非常相似,但它没有考虑到我的空值生成器必须基于定义类型上的方法。上面Dmytro建议的多边形方法似乎很有希望,但我仍然有一些问题。谢谢谢谢Dmytro,这是正确的方向!我在处理相同类型的定义时仍然有一个问题,比如val-DefA:Definition[Int]=Definition[Int]DefA;val DefB:Definition[Int]=Definition[Int]DefB这显然是两种不同的单例类型,但它们在隐式解析过程中混淆了编译器。证人在本案中有用吗?@iacoppace在您刚刚编写的内容中,没有单例类型,只是定义[Int]。case类InstancevalA:DefA.V,valB:DefB.V只是case类InstancevalA:Value[Int],valB:Value[String],这里没有关于DefA,DefB,Value[Int],Value[String]的信息,不记得它们是DefA.V,DefB.V。似乎您希望使用案例类实例。。。定义为AST,这可以通过宏来完成。这完全有道理,你是对的!你认为通过使用object DefA扩展定义[Int]DefA和随后的case类InstancevalA:DefA.V来重新处理这个问题会有帮助吗?多亏了你的指点,我才能够让它工作——我更新了这个问题。我希望能更好地实施,欢迎任何建议!谢谢@顺便说一句,我用你的原始代码添加了宏。谢谢Dmytro,这是正确的方向!我在处理相同类型的定义时仍然有一个问题,比如val-DefA:Definition[Int]=Definition[Int]DefA;val DefB:Definition[Int]=Definition[Int]DefB这显然是两种不同的单例类型,但它们在隐式解析过程中混淆了编译器。证人在本案中有用吗?@iacoppace在您刚刚编写的内容中,没有单例类型,只是定义[Int]。case类InstancevalA:DefA.V,valB:DefB.V只是case类InstancevalA:Value[Int],valB:Value[String],这里没有关于DefA,DefB,Value[Int],Value[String]的信息,不记得它们是DefA.V,DefB.V。似乎您希望使用案例类实例。。。定义为AST,这可以通过宏来完成。这完全有道理,你是对的!你认为通过使用object DefA扩展定义[Int]DefA和随后的case类InstancevalA:DefA.V来重新处理这个问题会有帮助吗?多亏了你的指点,我才能够让它工作——我更新了这个问题。我希望能更好地实施,欢迎任何建议!谢谢@顺便说一句,我添加了使用原始代码的宏。