Some Name
Some Name

Reputation: 9540

Avoiding instance of to filter by type

I currently have the following (not type safe) api which I'm trying to redesign in a typesafe way:

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())

Is there an approach to implement it typesafely? Are typemembers useful for such task or probbably shapeless can be used for that?

Upvotes: 2

Views: 124

Answers (1)

gregghz
gregghz

Reputation: 3965

I came up with two related approaches with shapeless. I'm not sure either will exactly meet your needs because they depend on having all the types of the elements of the list known ahead of time.

Assuming you have this stuff:

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

You can use filter on the HList directly:

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

If you want to avoid explicitly specifying TypeInfo in the filter call you can modify your filter function (but now you're required to give the HList type -- this can be worked around using a proxy class):

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

Upvotes: 2

Related Questions