类型列表上的类型级别映射

时间:2014-04-27 16:10:16

标签: scala shapeless type-level-computation

我有这个辅助功能:

def findByType[T: ClassTag](xs: Seq[Any]) =
  xs.find(classTag[T].runtimeClass.isInstance).map(_.asInstanceOf[T])
我目前正在使用的

val foo = findByType[Foo](xs)
val bar = findByType[Bar](xs)
val baz = findByType[Baz](xs)

然而,这里有一些重复;我喜欢什么(伪代码):

val List(foo, bar, baz) = List(Foo, Bar, Baz).map(t => findByType[t](xs))

我有什么选择?我可以按原样保留它,但如果有一些简单的东西可以干这个,我很乐意听到它。

2 个答案:

答案 0 :(得分:8)

如果你绝对必须这样做,Shapeless可以使它更清洁,更安全:

import shapeless._

def findByType[T](xs: Seq[Any])(implicit t: Typeable[T]) =
  xs.flatMap(t.cast).headOption

请注意,与您的实现不同,此实现不会对许多标准库的泛型类型给出错误答案:

scala> findByType[List[String]](Seq(List(1), List("a"), 'foo))
res3: Option[List[String]] = Some(List(a))

Vs以上。

scala> yourFindByType[List[String]](Seq(List(1), List("a"), 'foo))
res4: Option[List[String]] = Some(List(1))

(但是,您仍需要小心,因为这不适用于用户定义的泛型。)

它还可以让你做这样的好事:

trait Sifter[L <: HList] extends DepFn1[Seq[Any]] {
  type Out <: HList
}

object Sifter {
  type Aux[L <: HList, Out0 <: HList] = Sifter[L] { type Out = Out0 }

  implicit def emptySized: Aux[HNil, HNil] = new Sifter[HNil] {
    type Out = HNil
    def apply(xs: Seq[Any]) = HNil
  }

  implicit def otherSized[H, T <: HList, OutT <: HList](implicit
    typeable: Typeable[H],
    sifter: Aux[T, OutT]
  ): Aux[H :: T, Seq[H] :: OutT] = new Sifter[H :: T] {
    type Out = Seq[H] :: OutT
    def apply(xs: Seq[Any]) = xs.flatMap(typeable.cast) :: sifter(xs)
  }

  def sift[L <: HList](xs: Seq[Any])(implicit sifter: Sifter[L]): sifter.Out =
    sifter(xs)
}

然后:

scala> val myStuff: Seq[Any] = List(1, List('a', 'b'), "foo", 'bar)
myStuff: Seq[Any] = List(1, List(a, b), foo, 'bar)

scala> val myInts :: myCharLists :: myStrings :: HNil =
     |   Sifter.sift[Int :: List[Char] :: String :: HNil](myStuff)
myInts: Seq[Int] = List(1)
myCharLists: Seq[List[Char]] = List(List(a, b))
myStrings: Seq[String] = List(foo)

这基本上是你想要的类型安全版本。

答案 1 :(得分:2)

import scala.reflect.{ClassTag, classTag}
val List(foo, bar, baz) = List(classTag[Foo], classTag[Bar], classTag[Baz]).map(ct => findByType(xs)(ct))

当然,现在您丢失了类型信息 - foobarbaz都会有类型Option[Any]