在函数中包装 Shapeless FieldType 创建

Wrapping Shapeless FieldType creation in a function

有没有办法创建隐式 class 来为 return 与记录样式单例运算符 ->> 具有相同类型的 FieldType 提供自定义函数?

我想做类似的事情:

import shapeless.syntax.singleton._

implicit class FieldMaker[S <: Symbol](val s: S) {
  def make[T](t: T) = s ->> t
}

所以下面两个值有相同的类型:

val first  = 'test ->> Foo("bar")
val second = 'test make Foo("bar")

在之前的尝试中,我一直被 mkSingletonOps 中的宏所挫败。任何建议都会有所帮助!

更新:

这样做的动机源于创建 DSL 并试图仔细控制其语法。上面的简化示例跳过了此隐式 class 在 DSL 中实现的目的——具体来说,将函数应用于 T returning 类型 class,这在 DSL 的其他地方是需要的。

一个更典型的案例是:

import shapeless.syntax.singleton._

def func(t: T): SomeTypeclass[T] = _  // elided

implicit class FieldMaker[S <: Symbol](val s: S) {
  def make[T](t: T) = s ->> func(t)
}

所以下面两个值有相同的类型:

val first  = 'test ->> func(Foo("bar"))
val second = 'test make Foo("bar")

分配给 second 的表达式是 DSL 所需的语法。

可能没有。

  import shapeless.Witness
  import shapeless.labelled.FieldType
  import shapeless.syntax.singleton._

  implicit class FieldMaker[S <: Symbol](val s: S) {
    def make[T](t: T): FieldType[s.type, T] = s ->>[T] t
  }

  case class Foo(s: String)

  val first: FieldType[Witness.`'test`.T, Foo]  = 'test ->>[Foo] Foo("bar")
  val second: FieldType[fm.s.type, Foo] forSome { val fm: FieldMaker[Symbol] } = 
    'test make[Foo] Foo("bar")
  val third: FieldType[fm.s.type, Foo] forSome { val fm: FieldMaker[Witness.`'test`.T] } = 
    'test.narrow make[Foo] Foo("bar")

s ->>[T] t 的类型是 FieldType[s.type, T],这与类型参数 S 无关。 但是 s.type 中的 s 是什么?在隐式转换之前它是 'test 但之后它只是 FieldMaker[S] 实例的一个字段(实例,仅在隐式转换期间存在)所以我们有存在类型。一旦我们有了存在类型,我们就不能回到 'test.

这就是隐式的工作原理。


实际上我终于找到了制作与first 相同类型的second的方法(尽管它使用.narrow。我用类型 class 和隐式转换替换了隐式转换。

  import shapeless.Witness
  import shapeless.labelled.FieldType
  import shapeless.syntax.singleton._
  import shapeless.tag.@@

  trait FieldMaker[S <: Symbol, T] {
    def make(t: T): FieldType[S, T]
  }

  object FieldMaker {
    implicit def mkFieldMaker[/*S <: Symbol*/U, T](implicit
                                                   witness: Witness.Aux[/*S*/Symbol @@ U]
                                                  ): FieldMaker[/*S*/Symbol @@ U, T] = {
      val name: /*S*/Symbol @@ U = witness.value
      new FieldMaker[/*S*/Symbol @@ U, T] {
        override def make(t: T): FieldType[/*S*/Symbol @@ U, T] =
          (name ->>[T] t).asInstanceOf[FieldType[/*S*/Symbol @@ U, T]]
      }
    }

    object op {
      implicit class FieldMakerOp[S <: Symbol](s: S) {
        def make[T](t: T)(implicit fm: FieldMaker[S, T]): FieldType[S, T] = fm.make(t)
      }
    }

  }

  case class Foo(s: String)

  val first: FieldType[Witness.`'test`.T, Foo]  = 'test ->>[Foo] Foo("bar")

  import FieldMaker.op._
  val second: FieldType[Witness.`'test`.T, Foo] = 'test.narrow make/*[Foo]*/ Foo("bar")