【问题标题】:could not find implicit value for shapeless.ops.record.Selector找不到 shapeless.ops.record.Selector 的隐含值
【发布时间】:2026-02-11 23:45:01
【问题描述】:

我正在使用 shapeless 来探索在无形状标签记录中提取值类型的可能性。动机是我可以使用类型类和隐式来根据类型分派流程。 但是,下面的代码不起作用。

import shapeless._, record._
import shapeless.ops.record.Selector
import shapeless.syntax.RecordOps
import shapeless.syntax.singleton._

object ShapelessRecordTest extends App {

  trait Extractor[K, T] {
    type OUT_K
    def extract: T => OUT_K
  }

  object Extractor {

    type Aux[K, T, OUT_K0] = Extractor[K, T] {
      type OUT_K = OUT_K0
    }

    implicit def apply[T, Repr, OUT](k: Witness)(
      implicit gen: LabelledGeneric.Aux[T, Repr],
      selector: Selector.Aux[Repr, k.T, OUT]
    ): Aux[k.T, T, OUT] = new Extractor[k.T, T] {
      type OUT_K = OUT
      def extract: T => OUT_K = (e: T) => {
        val rep = gen.to(e)
        val rprOp: RecordOps[Repr] = new RecordOps[Repr](rep)
        rprOp(k)
      }
    }
  }

  case class Person(name: String, address: String, age: Int)

  implicit val gen = LabelledGeneric[Person]

  val nameWit: Witness = 'name
  val nameExtractor: Extractor.Aux[nameWit.T, Person, String] = Extractor(nameWit)

  def main(args: Array[String]): Unit = {
    val joe = Person("Joe", "Brighton", 33)
    println(nameExtractor.extract(joe))
  }

}

我使用的是 Scala 2.12.8,编译器错误是

{
    "resource": "/workspace/connecterra/stream-data-pipeline/flink/src/main/scala/io/connecterra/ShapelessRecordTest.scala",
    "owner": "_generated_diagnostic_collection_name_#0",
    "severity": 8,
    "message": "could not find implicit value for parameter selector: shapeless.ops.record.Selector.Aux[Repr,ShapelessRecordTest.nameWit.T,OUT]",
    "source": "scalac",
    "startLineNumber": 37,
    "startColumn": 74,
    "endLineNumber": 37,
    "endColumn": 75
}

我想了解我在这里缺少什么。还是完全有可能是无形的?

【问题讨论】:

    标签: scala implicit shapeless labelled-generic


    【解决方案1】:

    如果您要显式调用 apply,则将其设为隐式并没有多大意义。最好不要混合定义类型类的实例化方法和隐式方法。 RecordOps 不应该手动创建(有 import shapeless.record._ )。有时最好直接调用类型类的apply 方法(例如shapeless.ops.record.Selector),而不是依赖扩展方法。当您写 val nameWit: Witness = 'name 时,您会松散类型细化。创建implicit val gen = LabelledGeneric[Person] 毫无意义。对于SelectorRepr 应该是<: HList

    试试

    import shapeless._
    import shapeless.ops.record.Selector
    
    object ShapelessRecordTest {
    
      trait Extractor[K, T] {
        type OUT_K
        def extract: T => OUT_K
      }
    
      object Extractor {
    
        type Aux[K, T, OUT_K0] = Extractor[K, T] {
          type OUT_K = OUT_K0
        }
    
        def apply[T](k: Witness)(implicit 
          extractor: Extractor[k.T, T]): Aux[k.T, T, extractor.OUT_K] = extractor
    
        implicit def extractor[T, Repr <: HList, K](implicit
          gen: LabelledGeneric.Aux[T, Repr],
          selector: Selector[Repr, K]
        ): Aux[K, T, selector.Out] = new Extractor[K, T] {
          type OUT_K = selector.Out
          def extract: T => OUT_K = (e: T) => {
            val rep: Repr = gen.to(e)
            selector(rep)
          }
        }
      }
    
      case class Person(name: String, address: String, age: Int)
    
      val nameWit = Witness('name)
      val nameExtractor: Extractor.Aux[nameWit.T, Person, String] = Extractor(nameWit)
    
      def main(args: Array[String]): Unit = {
        val joe = Person("Joe", "Brighton", 33)
        println(nameExtractor.extract(joe)) // Joe
      }
    }
    

    【讨论】: