Shapeless:IsHCons,隐式未找到
Shapeless: IsHCons, implicit not found
我正在努力使这个相当愚蠢的示例起作用,然后计划将其扩展到更有意义的东西。
但到目前为止运气不好:我得到 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)
当您写 IsHCons.Aux[L, TypedMap, L]
时,您是在寻求 hlist L
有头 TypedMap
和尾 L
的证据,这意味着它是一个无限的 hlist ,这是不可能的,因为 Scala 不允许这种任意递归类型(例如,尝试编写类似 type Foo = Int :: Foo
的东西——你会得到一个 "illegal cyclic reference" 错误)。这也可能不是你想要的。
一般来说,您不太可能在 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
但第二种显然更可取(它更清晰,它不需要在编译时找到额外的类型 class 实例等),而且几乎总是可以编写任何你想要的东西正在尝试这样做。
另请注意,需要一个 IsHCons
实例意味着这里的递归将不会像声明的那样工作——你不能在 HNil
上调用 get
,因为编译器可以不要证明它是一个 HCons
(因为它不是)。
您确定需要 hlist 吗?如果您要求 hlist 的所有成员都是 TypedMap
类型,您不妨使用 Shapeless 的 Sized
(如果您希望该类型捕获长度)或者甚至只是一个普通的旧 List
.
如果您真的非常想在这里使用 HList
,我建议您写一个新类型 class:
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 上,您知道如何对其执行操作,等等。
我正在努力使这个相当愚蠢的示例起作用,然后计划将其扩展到更有意义的东西。
但到目前为止运气不好:我得到 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)
当您写 IsHCons.Aux[L, TypedMap, L]
时,您是在寻求 hlist L
有头 TypedMap
和尾 L
的证据,这意味着它是一个无限的 hlist ,这是不可能的,因为 Scala 不允许这种任意递归类型(例如,尝试编写类似 type Foo = Int :: Foo
的东西——你会得到一个 "illegal cyclic reference" 错误)。这也可能不是你想要的。
一般来说,您不太可能在 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
但第二种显然更可取(它更清晰,它不需要在编译时找到额外的类型 class 实例等),而且几乎总是可以编写任何你想要的东西正在尝试这样做。
另请注意,需要一个 IsHCons
实例意味着这里的递归将不会像声明的那样工作——你不能在 HNil
上调用 get
,因为编译器可以不要证明它是一个 HCons
(因为它不是)。
您确定需要 hlist 吗?如果您要求 hlist 的所有成员都是 TypedMap
类型,您不妨使用 Shapeless 的 Sized
(如果您希望该类型捕获长度)或者甚至只是一个普通的旧 List
.
如果您真的非常想在这里使用 HList
,我建议您写一个新类型 class:
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 上,您知道如何对其执行操作,等等。