Реализация HList
в бесформенном достаточно богата, чтобы включать функции HList
и KList
. Он обеспечивает операцию map
, которая применяет функцию более высокого ранга, возможно, с типо-специфическими случаями, к ее элементам, получая соответственно типизированный HList
результат,
import shapeless.Poly._
import shapeless.HList._
// Define a higher-ranked function from Sets to Options
object choose extends (Set ~> Option) {
def default[T](s : Set[T]) = s.headOption
}
// An HList of Sets
val sets = Set(1) :: Set("foo") :: HNil
// Map our choose function across it ...
val opts = sets map choose
// The resulting value
opts == Option(1) :: Option("foo") :: HNil
Обратите внимание, что, хотя в приведенном выше примере это не так, не требуется, чтобы элементы HList совместно использовали общий конструктор внешнего типа, это просто тот случай, когда функция с более высоким рейтингом, отображаемая с, имеет регистры для всех задействованных типов.
// size is a higher-ranked function from values of arbitrary type to a 'size'
// which is defined as 1 by default but which has type specific cases for
// Strings and tuples
object size extends (Id ~> Const[Int]#λ) {
def default[T](t : T) = 1
}
implicit def sizeString = size.λ[String](s => s.length)
implicit def sizeTuple[T, U](implicit st : size.λ[T], su : size.λ[U]) =
size.λ[(T, U)](t => 1+size(t._1)+size(t._2))
size(23) == 1 // Default
size("foo") == 3 // Type specific case for Strings
size((23, "foo")) == 5 // Type specific case for tuples
Теперь давайте сопоставим это с HList
,
val l = 23 :: true :: "foo" :: ("bar", "wibble") :: HNil
val ls = l map size
ls == 1 :: 1 :: 3 :: 10 :: HNil
В этом случае тип результата отображаемой функции является константой: это Int, независимо от типа аргумента. Следовательно, полученный HList имеет элементы одного и того же типа, что означает, что он может быть с пользой преобразован в простой список
ls.toList == List(1, 1, 3, 10)