将代数日期类型列表拆分为分支列表?

Split list of algebraic date type to lists of branches?

我对 shapeless 还很陌生,所以这个问题可能很简单。

这是 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,因此无法区分元素 ABC... 仅使用 compile-time 技术(无形、类型 类、隐式、宏、compile-time 反射)。这些元素仅在运行时可区分,因此您必须使用一些运行时技术(模式匹配、转换、运行时反射)。

尝试使用运行时反射split进入地图

def split(lst: List[Test]): Map[String, List[Test]]  =
  lst.groupBy(_.getClass.getSimpleName)

split(List(C(), B(), A(), C(), B(), A()))
// HashMap(A -> List(A(), A()), B -> List(B(), B()), C -> List(C(), C()))

split 使用 Shapeless + 运行时反射

变成 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, V], default: V): Option[R]
}
object FromMapWithDefault {
  implicit def hnilFromMap[T]: FromMapWithDefault[HNil] =
    new FromMapWithDefault[HNil] {
      def apply[K, V](m: Map[K, V], 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, V](m: Map[K, V], 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(), B(), A(), C(), B(), A())) 
// List(A(), A()) :: List(B(), B()) :: List(C(), C()) :: List() :: HNil
res: List[A] :: List[B] :: List[C] :: List[Z] :: HNil