问题描述
我是无形的新手,所以这个问题可能很容易。
这是ADT:
sealed trait Test
final case class A() extends Test
final case class B() extends Test
final case class C() extends Test
...
final case class Z() extends Test
是否可以编写没有 繁琐 模式匹配的函数?
def split(lst: List[Test]): List[A] :: List[B] :: ... :: HNil = //
解决方法
在编译时,List
的所有元素都具有相同的静态类型Test
,因此无法区分元素A
,B
,C
...仅使用编译时技术(无形状,类型类,隐式,宏,编译时反射)。这些元素仅在运行时是可区分的,因此您必须使用某种运行时技术(模式匹配,转换,运行时反射)。
Why Does This Type Constraint Fail for List[Seq[AnyVal or String]]
Scala: verify class parameter is not instanceOf a trait at compile time
flatMap with Shapeless yield FlatMapper not found
尝试使用运行时反射将split
插入地图
def split(lst: List[Test]): Map[String,List[Test]] =
lst.groupBy(_.getClass.getSimpleName)
split(List(C(),B(),A(),C(),A()))
// HashMap(A -> List(A(),A()),B -> List(B(),B()),C -> List(C(),C()))
使用Shapeless +运行时反射将或split
转换为HList
import shapeless.labelled.{FieldType,field}
import shapeless.{::,Coproduct,HList,HNil,LabelledGeneric,Poly1,Typeable,Witness}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.Mapper
import shapeless.ops.record.Values
import shapeless.record._
import scala.annotation.implicitNotFound
object listPoly extends Poly1 {
implicit def cse[K <: Symbol,V]: Case.Aux[FieldType[K,V],FieldType[K,List[V]]] = null
}
// modified shapeless.ops.maps.FromMap
@implicitNotFound("Implicit not found: FromMapWithDefault[${R}]. Maps can only be converted to appropriate Record types.")
trait FromMapWithDefault[R <: HList] extends Serializable {
// if no value by this key use default,if can't cast return None
def apply[K,V](m: Map[K,default: V): Option[R]
}
object FromMapWithDefault {
implicit def hnilFromMap[T]: FromMapWithDefault[HNil] =
new FromMapWithDefault[HNil] {
def apply[K,default: V): Option[HNil] = Some(HNil)
}
implicit def hlistFromMap[K0,V0,T <: HList]
(implicit wk: Witness.Aux[K0],tv: Typeable[V0],fmt: FromMapWithDefault[T]): FromMapWithDefault[FieldType[K0,V0] :: T] =
new FromMapWithDefault[FieldType[K0,V0] :: T] {
def apply[K,default: V): Option[FieldType[K0,V0] :: T] = {
val value = m.getOrElse(wk.value.asInstanceOf[K],default)
for {
typed <- tv.cast(value)
rest <- fmt(m,default)
} yield field[K0](typed) :: rest
}
}
}
def split[T,C <: Coproduct,L <: HList,L1 <: HList](lst: List[T])(
implicit
labelledGeneric: LabelledGeneric.Aux[T,C],toHList: ToHList.Aux[C,L],mapper: Mapper.Aux[listPoly.type,L,L1],fromMapWithDefault: FromMapWithDefault[L1],values: Values[L1]
): values.Out = {
val groupped = lst.groupBy(_.getClass.getSimpleName).map { case (k,v) => Symbol(k) -> v }
fromMapWithDefault(groupped,Nil).get.values
}
测试:
sealed trait Test
final case class A() extends Test
final case class B() extends Test
final case class C() extends Test
final case class Z() extends Test
val res = split(List[Test](C(),A()))
// List(A(),A()) :: List(B(),B()) :: List(C(),C()) :: List() :: HNil
res: List[A] :: List[B] :: List[C] :: List[Z] :: HNil