在 scala shapeless 库中,是否有一种本地方法可以将产品类型(HList/Generic/NamedGeneric)转换为等于其元数的 int 单例类型?

In scala shapeless library, is there a native way to convert a product type (HList/Generic/NamedGeneric) to an int singleton type equal to its arity?

我正在尝试弄清楚如何将 shapeless 的特征(单例类型)与另一个特征(HList)相关联,假设我们想派生一个泛型类型 Vector,其中包含关于输入向量:

package com.tribbloids.spike.shapeless_spike.shapesafe

import shapeless.{Generic, Witness}

import scala.language.implicitConversions

trait Vector[W <: Witness.Lt[Int]] extends Serializable {

  def witness: W

  def <*>(that: Vector[W]): Vector[W] = ???
}

object Vector {

  case class Impl[W <: Witness.Lt[Int]](witness: W) extends Vector[W] {}

  def zeros[W <: Witness.Lt[Int]](witness: W): Impl[W] = Impl(witness)

  object Attempt1 {

    def values(v: (Double)) = Impl(Witness(1))
    def values(v: (Double, Double)) = Impl(Witness(2))
    def values(v: (Double, Double, Double)) = Impl(Witness(3))

    Vector.zeros(Witness(2)) <*> values(2.0, 3.0)

    Vector.zeros(Witness(3)) <*> values(2.0, 3.0) // breaks as expected
  }
}

我可以写很多行来支持派生(这最终会达到 JVM 对类加载的限制),或者我可以写一个更简洁的隐式转换来按需动态创建单例类型,像这样:

  object Attempt2 {
    type HInts = Int :: HInts

    def values[T <: Product](v: T)(implicit aux: Generic.Aux[T, HInts]) = {
      ???
    }
  }

然而,尽管两者都是非常成熟的无形特征,但似乎没有文档教如何启用这种代数类型推导。

有没有一种简单的方法可以用足够通用的语句替换我所有的隐式函数?我现在正在使用 scala-2.12 + shapeless 2.3。

非常感谢您提供的信息。

如果你只需要

  • 能够在代码中将产品大小表达为文字
  • 在编译时强制执行该大小

如果你使用 Nat 而不是 Witness.Lt[Int] 就容易多了:

~ amm
Loading...
Welcome to the Ammonite Repl 2.0.4 (Scala 2.13.1 Java 1.8.0_242)
@  import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._
import $ivy.$                             , shapeless._

@ {
  trait OfSize[A, N <: Nat]
  object OfSize {

    implicit def evidence[A, Repr <: HList, N <: Nat](
      implicit gen: Generic.Aux[A, Repr],
      length: shapeless.ops.hlist.Length.Aux[Repr, N]
    ): OfSize[A, N] = new OfSize[A, N] {}
  }
  }
defined trait OfSize
defined object OfSize

@ def needSized[A, N <: Nat](a: A, n: N)(implicit ev: A OfSize N) = "OK"
defined function needSized

@ case class Test(a: String, b: Int)
defined class Test

@ needSized(Test("a", 0), Nat(3))
cmd4.sc:1: could not find implicit value for parameter ev: ammonite.$sess.cmd1.OfSize[ammonite.$sess.cmd3.Test,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
val res4 = needSized(Test("a", 0), Nat(3))
                    ^
Compilation Failed

@ needSized(Test("a", 0), Nat(2))
res4: String = "OK"

@

我相信 Nat 某些 for 将在 Shapeless 3 中可用,因此它比依赖见证更可移植。但是,如果您想使用 Witness,那么我相信需要提供您自己的宏以将 Witness 转换为 Nat,然后使用现有的无形实用程序。