2016-03-11 3 views
1

В моем приложении у меня есть несколько классов и объектов класса, которые являются частью запечатанной иерархии признаков. Я использую их как сообщения в Акке.Предоставить импликации для всех подтипов запечатанного типа

Эти классы должны быть преобразованы в удобную для пользователя форму перед отправкой через websocket.

Раньше я использовал большой матч шаблон, чтобы преобразовать их в одном месте, но количество типов растет, я хотел бы использовать неявное преобразование:

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 [email protected] => 
     implicitly[Converter[SubType1.type]].convert(t1) 
     case [email protected] => 
     implicitly[Converter[SubType2.type]].convert(t2) 
     case [email protected] => 
     implicitly[Converter[SubType3.type]].convert(t3) 
    } 
    } 
} 

Я хотел бы использовать их следующим образом:

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 автоматически без написания макроса. Может быть, есть уже библиотека, которая предоставляет макрос вот так?

ответ

0

Поскольку у вас нет информации во время компиляции о типе t, вы должны работать во время выполнения.

если вы кладете преобразователи в sealed family, вы могли бы сделать что-то вроде follwing, используя метод объясняется в 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) 
} 
+0

Это definitelly хорошее решение, но не один я искал, как это не проверяется временем компиляции. Я постараюсь понять, что здесь происходит, так как это выглядит очень полезно. Благодаря! –

+0

Единственное, что вы не можете проверить, это если у вас есть экземпляр typeclass для каждого из объектов вашего случая. Если вы найдете способ сделать это, дайте мне знать! :) –

+0

Я сделал что-то вроде этого с помощью макроса: https://github.com/lustefaniak/explicit-implicits –

Смежные вопросы