All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thoughtworks.feature.Demixin.scala Maven / Gradle / Ivy

package com.thoughtworks.feature
import shapeless._
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

/** A type class that converts a mix-in type to [[shapeless.HList]].
  *
  *
  * == Common imports ==
  *
  * You may want to use [[Demixin]] with [[shapeless.HList]].
  *
  * {{{
  * import shapeless._
  * }}}
  *
  * @example [[Out]] of [[Demixin]] on non-mixed-in types other than [[scala.Any]] should be a [[shapeless.HList]] that contains only one element
  *
  *          {{{
  *          val demixin = Demixin[String]
  *          "implicitly[demixin.Out =:= (String :: HNil)]" should compile
  *          }}}
  *
  * @example [[Out]] of [[Demixin]] on [[scala.Any]] should be [[shapeless.HNil]]
  *
  *          {{{
  *          val demixin = Demixin[Any]
  *          "implicitly[demixin.Out =:= HNil]" should compile
  *          }}}
  *
  * @example The [[Demixin]] type class can be summoned from [[Demixin.apply]] method:
  *
  *          {{{
  *          class A; trait B; object C;
  *          val demixin = Demixin[A with B with C.type with String with Int]
  *          }}}
  *
  *          [[Out]] should be a [[shapeless.HList]] of each type components in the mix-in type `ConjunctionType`.
  *
  *          {{{
  *          "implicitly[demixin.Out =:= (A :: B :: C.type :: String :: Int :: HNil)]" should compile
  *          }}}
  *
  *          The elements in [[Out]] should keep the same order as type components in `ConjunctionType`.
  *
  *          {{{
  *          "implicitly[demixin.Out =:!= (String :: A :: B :: C.type :: Int :: HNil)]" should compile
  *          }}}
  */
trait Demixin[ConjunctionType] {
  type Out <: HList
}

object Demixin {

  type Aux[ConjunctionType, Out0] = Demixin[ConjunctionType] {
    type Out = Out0
  }

  def apply[ConjunctionType](implicit demixin: Demixin[ConjunctionType]): Demixin.Aux[ConjunctionType, demixin.Out] =
    demixin

  implicit def materialize[ConjunctionType]: Demixin[ConjunctionType] =
    macro Macros.materialize[ConjunctionType]

  private[Demixin] final class Macros(val c: whitebox.Context) extends CaseClassMacros {
    import c.universe._

    private def demixin(t: Type): Stream[Type] = {
      t.dealias match {
        case RefinedType(superTypes, refinedScope) if refinedScope.isEmpty =>
          superTypes.toStream.flatMap(demixin)
        case any if definitions.AnyTpe <:< any =>
          Stream.empty[Type]
        case notRefinedType =>
          Stream(notRefinedType)
      }
    }

    def materialize[ConjunctionType: WeakTypeTag]: Tree = {
      val conjunctionType = weakTypeOf[ConjunctionType]
      val out = mkHListTpe(demixin(conjunctionType).distinct.toList)
      val result = q"""
        new _root_.com.thoughtworks.feature.Demixin[$conjunctionType] {
          type Out = $out
        } : _root_.com.thoughtworks.feature.Demixin.Aux[$conjunctionType, $out]
      """
//      c.info(c.enclosingPosition, showCode(result), true)
      result
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy