为密封类型的所有子类型提供隐式 [英] Provide implicits for all subtypes of sealed type

查看:50
本文介绍了为密封类型的所有子类型提供隐式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我有多个案例类和对象,它们是密封特征层次结构的一部分.我将它们用作 Akka 中的消息.

In my application I have multiple case classes and objects which are part of sealed trait hierarchy. I use them as messages in Akka.

在通过 websocket 发送之前,这些类需要转换为用户友好的形式.

Those classes need to be converted to user friendly form before sending through websocket.

以前我使用大模式匹配在一个地方转换它们,但随着类型数量的增加,我想使用隐式转换:

Previously I used big pattern match to convert them in single place, but as number of types grows I would like to use implicit conversion:

object Types {
  sealed trait Type
  case object SubType1 extends Type
  case object SubType2 extends Type
  case object SubType3 extends Type

  trait Converter[T] {
    def convert(t: T): Int
  }

}

object Implicits {
  import Types._
  implicit object Type1Coverter extends Converter[SubType1.type] {
    override def convert(t: SubType1.type): Int = 1
  }
  implicit object Type2Coverter extends Converter[SubType2.type] {
    override def convert(t: SubType2.type): Int = 2
  }
  implicit object Type3Coverter extends Converter[SubType3.type] {
    override def convert(t: SubType3.type): Int = 3
  }
}

object Conversion {
  import Types._
  def convert[T: Converter](t: T): Int = {
    implicitly[Converter[T]].convert(t)
  }

  def convert2[T <: Type](t: T)(implicit ev1: Converter[SubType1.type], ev2: Converter[SubType2.type], ev3: Converter[SubType3.type]): Int = {
    t match {
      case t1@SubType1 =>
        implicitly[Converter[SubType1.type]].convert(t1)
      case t2@SubType2 =>
        implicitly[Converter[SubType2.type]].convert(t2)
      case t3@SubType3 =>
        implicitly[Converter[SubType3.type]].convert(t3)
    }
  }
}

我想按如下方式使用它们:

I would like to use them as follow:

import Types._
import Conversion._
import Implicits._

val t1 = SubType1
val x1: Int = convert(t1)

val t: Type = SubType2 // T is of type Type
//Is it possible to handle that?
//val x: Int = convert(t)

val y: Int = convert2(t)

我很想知道是否有任何神奇"的方法可以在不编写宏的情况下自动生成诸如 convert2 之类的东西.也许已经有一个库提供这样的宏?

I would love to know if there is any "magic" way to generate something like convert2 automatically without writing a macro. Maybe there is already a library which provides macro like this?

推荐答案

由于您在编译时没有关于 t 类型的信息,因此您必须在运行时工作.

Since you have no info at compile time about t's type, you have to work at runtime.

如果您将转换器放在 密封家庭,您可以使用 这个问题:

if you put your Converters in a sealed family, you could do something like the follwing, using a technique explained in this question:

import shapeless._

trait AllSingletons[A, C <: Coproduct] {
  def values: List[A]
}

object AllSingletons {
  implicit def cnilSingletons[A]: AllSingletons[A, CNil] =
    new AllSingletons[A, CNil] {
      def values = Nil
    }

  implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit
                                                              tsc: AllSingletons[A, T],
                                                              witness: Witness.Aux[H]): AllSingletons[A, H :+: T] =
    new AllSingletons[A, H :+: T] {
      def values = witness.value :: tsc.values
    }
}

trait EnumerableAdt[A] {
  def values: Set[A]
}

object EnumerableAdt {
  implicit def fromAllSingletons[A, C <: Coproduct](implicit
                                                    gen: Generic.Aux[A, C],
                                                    singletons: AllSingletons[A, C]): EnumerableAdt[A] =
    new EnumerableAdt[A] {
      def values = singletons.values.toSet
    }
}

object Types {
  sealed trait Type
  case object SubType1 extends Type
  case object SubType2 extends Type
  case object SubType3 extends Type

  sealed abstract class Converter[T <: Type: ClassTag] {
    def convert(t: T): Int
    def convertibleObjectClass = implicitly[ClassTag[T]].runtimeClass
  }

  object Implicits {
    implicit object Type1Converter extends Converter[SubType1.type] {
      override def convert(t: SubType1.type): Int = 1
    }
    implicit object Type2Converter extends Converter[SubType2.type] {
      override def convert(t: SubType2.type): Int = 2
    }
    // let's pretend you FORGOT to add Type3Converter
    //    implicit object Type3Converter extends Converter[SubType3.type] {
    //      override def convert(t: SubType3.type): Int = 3
    //    }
  }
}


object Conversion {
  import Types._
  val AllConverters: Map[Class[_], Converter[_ <: Type]] = implicitly[EnumerableAdt[Converter[_ <: Type]]].values
    .map(c => c.convertibleObjectClass -> c).toMap

  // You're sure you have all the converters here but you can't be sure you remembered to add one per subtype... you have to test it
  // you are sure this cast doesn't fail anyway because of how you created the map
  def findConverter[T <: Type](t: T) = AllConverters.get(t.getClass).asInstanceOf[Option[Converter[T]]]

  def convert2[T <: Type](t: T): Option[Int] = findConverter(t).map(_.convert(t))
}

object Test extends App {
  import Types._
  import Conversion._

  val t: Type = SubType2
  val t2: Type = SubType3

  // works, because a Converter[Subtype2.type] exists
  val a: Option[Int] = convert2(t)
  // returns None, because a Converter[Subtype3.type] doesn't exist
  val b: Option[Int] = convert2(t2)
}

这篇关于为密封类型的所有子类型提供隐式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆