避免按类型过滤的实例

问题描述

我目前有以下(不是类型安全的)api,我正在尝试以类型安全的方式进行重新设计:

import cats.instances.list._
import cats.Syntax.functorFilter._


sealed trait EnumType
case object A extends EnumType
case object B extends EnumType
case object C extends EnumType

sealed abstract class TypeInfo[T <: EnumType](val enumType: T)
case class Ainfo() extends TypeInfo(A)
case class Ainfo2() extends TypeInfo(A)
case class Binfo() extends TypeInfo(B)
case class Cinfo() extends TypeInfo(C)

//This is the function implemented in a not typesafe way
def filterByEnumType[T <: EnumType: classtag](lst: List[TypeInfo[_]]): List[TypeInfo[T]] = {
  lst mapFilter { info =>
    info.enumType match {
      case _: T => Some(info.asInstanceOf[TypeInfo[T]]) //not type safe
      case _    => None
    }
  }
}

filterByEnumType[A.type](List(Ainfo(),Binfo(),Ainfo2(),Cinfo()))  //List(Ainfo(),Ainfo2())

有没有一种方法可以安全地实现它?键入成员是否对此类任务有用,或者可能shapeless可以用于该任务?

解决方法

我想出了两种不相关的相关方法。我不确定两者是否都能完全满足您的需求,因为它们取决于提前知道列表中所有类型的元素。

假设您拥有这些东西:

import shapeless._
import shapeless.ops.hlist._

type HType = TypeInfo[A.type] :: TypeInfo[B.type] :: TypeInfo[A.type] :: TypeInfo[C.type] :: HNil
val hlist: HType = Ainfo() :: Binfo() :: Ainfo2() :: Cinfo() :: HNil

您可以直接在HList上使用过滤器:

hlist.filter[TypeInfo[A.type]] // Ainfo() :: Ainfo2() :: HNil

如果要避免在过滤器调用中显式指定TypeInfo,则可以修改过滤器功能(但现在需要提供HList类型-可以使用代理类来解决此问题):

def filterByEnumType[T <: EnumType,L <: HList](
    list: L
)(implicit filter: Filter[L,TypeInfo[T]]): filter.Out = {
  filter.apply(list)
}

filterByEnumType[A.type,HType](hlist) // Ainfo() :: Ainfo2() :: HNil