Scala 使用无形状的通用默认实例为案例类构建,并使用定义公共createValue方法的参数

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[

我正在努力实现以下目标——使用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[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来重新处理这个问题会有帮助吗?多亏了你的指点,我才能够让它工作——我更新了这个问题。我希望能更好地实施,欢迎任何建议!谢谢@顺便说一句,我添加了使用原始代码的宏。