【问题标题】:Shapeless: IsHCons, implicit not found无形:IsHCons,未找到隐式
【发布时间】:2015-03-30 02:19:28
【问题描述】:

我正在尝试使这个相当愚蠢的示例工作,然后计划将其扩展到更有意义的东西。

但到目前为止还没有运气:我得到了could not find implicit value for parameter ihc

我错过了什么?

sealed trait Field[T] { def name: String }
case class IntegerField(name: String) extends Field[Int]
val year = IntegerField("year")

val test = (year :: 23 :: HNil) :: (year :: 2 :: HNil) :: HNil

type TypedMap = IntegerField ::  Int  :: HNil

def get[L <: HList : <<:[TypedMap]#λ]
      (key: IntegerField, list: L)
      (implicit ihc: IsHCons.Aux[L, TypedMap, L]
    ): Option[Int] = {

  if( list == HNil ) return None
  val elem: TypedMap = list.head
  if( elem.head == key ) Some(elem.tail.head)
  else get(key, list.tail)

}

get(year, test)

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    当您编写 IsHCons.Aux[L, TypedMap, L] 时,您要求提供证据证明 hlist L 具有头部 TypedMap 和尾部 L,这意味着它是一个无限的 hlist,这是不可能的,因为 Scala 没有' 不允许这种任意递归类型(例如,尝试编写 type Foo = Int :: Foo 之类的东西——你会得到一个“非法循环引用”错误)。这也可能不是你想要的。

    一般来说,你不太可能在 Shapeless 中使用 IsHCons,因为在类型中指明你想要的结构几乎总是更好。例如,以下两个定义做同样的事情:

    import shapeless._, ops.hlist.IsHCons
    
    def foo[L <: HList](l: L)(implicit ev: IsHCons[L]) = ev.head(l)
    

    还有:

    def foo[H, T <: HList](l: H :: T) = l.head
    

    但是第二个显然更可取(它更清楚,它不需要在编译时找到额外的类型类实例等),并且几乎总是可以编写任何你想要这样做的东西.

    还请注意,需要 IsHCons 实例意味着此处的递归将无法按所述工作 - 您不能在 HNil 上调用 get,因为编译器无法证明它是 @987654332 @(因为不是)。

    你确定你需要一个 hlist 吗?如果您要求 hlist 的所有成员都是 TypedMap 类型,您不妨使用 Shapeless 的 Sized(如果您希望该类型捕获长度),甚至只是一个普通的旧 List

    如果您真的非常想在这里使用HList,我建议您编写一个新的类型类:

    trait FindField[L <: HList] {
      def find(key: IntegerField, l: L): Option[Int]
    }
    
    object FindField {
      implicit val findFieldHNil: FindField[HNil] = new FindField[HNil] {
        def find(key: IntegerField, l: HNil) = None
      }
    
      implicit def findFieldHCons[H <: TypedMap, T <: HList](implicit
        fft: FindField[T]
      ): FindField[H :: T] = new FindField[H :: T] {
        def find(key: IntegerField, l: H :: T) = if (l.head.head == key)
          Some(l.head.tail.head)
        else fft.find(key, l.tail)
      }
    }
    
    def get[L <: HList](key: IntegerField, l: L)(implicit
      ffl: FindField[L]
    ): Option[Int] = ffl.find(key, l)
    

    然后:

    scala> get(IntegerField("year"), test)
    res3: Option[Int] = Some(23)
    
    scala> get(IntegerField("foo"), test)
    res4: Option[Int] = None
    

    这是 Shapeless 中非常常见的模式——你归纳地描述如何对一个空的 hlist 执行操作,然后在一个 hlist 上执行一个头部附加到一个你知道如何执行操作的尾部的操作,等等。

    【讨论】:

    • 谢谢!我知道这样看起来很愚蠢。我的目标是构建类似无形HMap 的东西,但使用get() 函数接受通用Field[T] 实例而不是IntegerFieldStringField,...在您的示例中,如果您要查找的项目不是第一个?如果我尝试写else find(key, l.tail) 我会得到found : T, required: shapeless.::[H,T], --- 这就是我尝试IsHCons.Aux的原因
    • 啊,很好——我打错了,并更新了findFieldHCons,这样它就可以正常工作了。
    • 太棒了!即使是不简单的版本也像一个魅力。谢谢你:)
    • 很高兴它有帮助!我还刚刚更新了第一个比较,使其对IsHCons 更加公平(因为在这个简单的案例中您不需要指定头尾类型),但这个论点仍然成立。
    猜你喜欢
    • 1970-01-01
    • 2014-07-06
    • 2015-12-23
    • 1970-01-01
    • 2019-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多