Ответ 1
Определение 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]
.
Для map(f)
в A :: B :: ... :: HNil
компилятор должен найти implicits для 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
, одним из мест для поиска implicits является f
поля и методы. Поэтому компилятор найдет Case
, определенный в f
.