我在Shapeless的源代码和使用Shapeless的代码中看到了一个名为“at”的对象(可能是函数)。特别地,在回答这个问题时使用了它。下面是代码片段:
object iterateOverHList extends Poly1 {
implicit def iterable[T, L[T] <: Iterable[T]] = at[L[T]](_.iterator)
}
我有一些线索表明它与~>类型的apply方法有关。 "at"具体是做什么的,它在哪里定义?
我在Shapeless的源代码和使用Shapeless的代码中看到了一个名为“at”的对象(可能是函数)。特别地,在回答这个问题时使用了它。下面是代码片段:
object iterateOverHList extends Poly1 {
implicit def iterable[T, L[T] <: Iterable[T]] = at[L[T]](_.iterator)
}
我有一些线索表明它与~>类型的apply方法有关。 "at"具体是做什么的,它在哪里定义?
PolyN#at
的定义at
是一种与Poly
一起工作的通用方式。
~>
与apply
是Poly1
的特殊情况。这里使用apply
来定义使用at
的隐式方法:
implicit def caseUniv[T] = at[F[T]](apply(_))
方法at
在PolyN
(例如在Poly1
中)中定义,如下所示:链接
trait PolyN extends Poly { outer =>
type Case[T1, T2, ..., TN] = poly.Case[this.type, T1 :: T2 :: ... :: TN :: HNil]
object Case {
type Aux[T1, T2, ..., TN, Result0] = poly.Case[outer.type, T1 :: T2 :: ... :: TN :: HNil] { type Result = Result0 }
}
class CaseBuilder[T1, T2, ..., TN] {
def apply[Res](fn: (T1, T2, ..., TN) => Res) = new Case[T1, T2, ..., TN] {
type Result = Res
val value = (l: T1 :: T2 :: ... :: TN :: HNil) => l match {
case a1 :: a2 :: ... :: aN :: HNil => fn(a1, a2, ..., aN)
}
}
}
def at[T1, T2, ..., TN] = new CaseBuilder[T1, T2, ..., TN]
}
如果遇到 Poly1
的情况:
trait Poly1 extends Poly { outer =>
type Case[T1] = poly.Case[this.type, T1 :: HNil]
object Case {
type Aux[T1, Result0] = poly.Case[outer.type, T1 :: HNil] { type Result = Result0 }
}
class CaseBuilder[T1] {
def apply[Res](fn: (T1) => Res) = new Case[T1] {
type Result = Res
val value = (l: T1) => l match {
case a1 :: HNil => fn(a1)
}
}
}
def at[T1] = new CaseBuilder[T1]
}
at[Int]
创建了一个CaseBuilder[Int]
的实例,而at[Int].apply[String](_.toString)
或者at[Int](_.toString)
(apply
方法调用的语法糖)则创建了一个poly.Case[this.type, Int :: HNil]{ type Result = String }
的实例。implicit def iterable[T, L[T] <: Iterable[T]] = at[L[T]](_.iterator)
,你可以创建一个隐式方法来创建一个poly.Case[this.type, L[T] :: HNil]{ type Result = Iterator[T] }
。map
中(还有一些其他的多态函数)。
HList#map
的实现map
的定义如下:def map(f : Poly)(implicit mapper : Mapper[f.type, L]) : mapper.Out = mapper(l)
(L
是HList
类型)
为了创建一个Mapper
编译器,查找Case1[Fn, T]
。
对于A :: B :: ... :: HNil
上的map(f)
,编译器必须查找Case1[f.type, A]
、Case1[f.type, B]
等隐式参数。
在List[Int] :: HNil
的情况下,唯一需要的隐式Case1
是Case1[f.type, List[Int]]
。
请注意,Case1
是这样定义的:
type Case1[Fn, T] = Case[Fn, T :: HNil]
因此,我们需要为Case[f.type, List[Int] :: HNil]
找到一个隐式值。
如果f
是一个object
,编译器在搜索隐式值时会在f
的字段和方法中查找。因此编译器会找到f
中定义的Case
。
我不是专业人士,所以@miles-sabin和@travis-brown可以给出完整且更清晰的答案,但我也可以尝试一下(这并不完整,也没有展示所有正式问题):
iterateOverHList
是一个多态函数,扩展了Poly1
,如果你查看此(Poly1
)特质的实现,你会发现它只接受一个对象作为参数,类型为你的例子中的L[T]
;
函数at
的确意味着(请看下面的实现):“在L[T]
类型的情况下应用at
内部的函数”,因此在你的例子中是你对象的iterator
方法。
因此,您可以编写不同的隐式函数,可应用于不同的类型,当您使用不同和复杂的类型遍历HList
(例如使用map)时,这非常有用。
您可以在此处找到Poly
特质的实现以及我的上述说法的证明,例如:http://xuwei-k.github.io/shapeless-sxr/shapeless-2.10-2.0.0-M1/polyntraits.scala.html
在这里,我们可以看到Poly1
特质是:
trait Poly1 extends Poly { outer =>
type Case[A] = poly.Case[this.type, A :: HNil]
object Case {
type Aux[A, Result0] = poly.Case[outer.type, A :: HNil] { type Result = Result0 }
}
class CaseBuilder[A] {
def apply[Res](fn: (A) => Res) = new Case[A] {
type Result = Res
val value = (l : A :: HNil) => l match { case a :: HNil => fn(a) }
}
}
def at[A] = new CaseBuilder[A]
}