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

enum.Values.scala Maven / Gradle / Ivy

The newest version!
package enum

import shapeless.{:+:, Witness, CNil, Generic, Coproduct}

import scala.annotation.implicitNotFound

/** A typeclass giving the values of an enumeration */
@implicitNotFound("Unable to find values of ${A}. Make sure it is a sealed trait and is only extended by case objects.")
trait Values[A] {
  /** The values of the `A` enumeration */
  val values: Set[A]
}

 /*
  * The companion object contains the machinery to automatically derive the values of a sealed trait
  * extended by case objects only.
  *
  * Basically, the derivation process is the following:
  *  - we are given a sort of list containing the types of the case objects that extend a sealed trait `A` ;
  *  - we inductively traverse this structure using two implicit definitions: one for the empty list case and
  *    one for the “cons” case ;
  *  - we accumulate the traversed case objects in a `List` ;
  *  - that’s it: the resulting `List` contains all the possible values of type `A`.
  *
  * The first step is the hard part of this process and is actually achieved by shapeless (using macros).
  */
object Values {

  @inline def apply[A](implicit values: Values[A]): Values[A] = values

  trait Derived[A] extends Values[A]

  /**
    * {{{
    *   sealed trait Foo
    *   object Foo {
    *     case object Bar extends Foo
    *     case object Baz extends Foo
    *   }
    *   Values.derived[Foo].values == Set(Foo.Bar, Foo.Baz)
    * }}}
    *
    * @return All the possible values of `A`
    */
  @inline implicit def derived[A](implicit derived: Derived[A]): Values[A] = derived

  object Derived {
     /*
      * Derives a `Values[A]` instance given a representation `Repr` of type `A` in terms of `Coproduct`, and a given
      * a `ValuesAux[A, Repr]` instance.
      *
      * @tparam Repr Type of the representation of `A` as a `Coproduct`.
      *              A `Coproduct` is recursively defined as either `CNil` or `H :+: T`, where `T` is a
      *              subtype of `Coproduct`.
      *              For instance, a sealed trait that is extended by only one case object `Foo` can have the following
      *              representation: `Foo.type :+: CNil`.
      * @param gen Isomorphism between `A` and `Repr`. Shapeless is able to provide such an implicit value for any
      *            sealed type
      */
    implicit def generic[A, Repr <: Coproduct](implicit gen: Generic.Aux[A, Repr], v: ValuesAux[A, Repr]): Derived[A] =
      new Derived[A] {
        val values = v.values.to[Set]
      }

     /*
      * An intermediate data structure that carries both the type `A` and its representation `Repr`.
      *
      * @tparam Repr Phantom type describing the structure of `A`
      */
    case class ValuesAux[A, Repr](values: List[A])

     /*
      * To sum up: we are given the types of the possible `A` values in a `Coproduct` structure and we want to
      * define how to traverse this structure to accumulate all the values in a `List`.
      */
    object ValuesAux {

       /*
        * Base case: no values
        */
      implicit def cnil[A]: ValuesAux[A, CNil] = ValuesAux[A, CNil](Nil)

       /*
        * Induction case: append a value of type `L` to the `R` previous values
        * @param l Singleton of type `L`
        */
      implicit def ccons[A, L <: A, R <: Coproduct](implicit l: Witness.Aux[L], r: ValuesAux[A, R]): ValuesAux[A, L :+: R] =
        ValuesAux[A, L :+: R](l.value :: r.values)

    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy