Last active
November 18, 2015 08:14
-
-
Save julienrf/dd96f5c58af391c07df2 to your computer and use it in GitHub Desktop.
Yet another Scala enumeration-like tool powered by shapeless. Inspired by https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/enum.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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.") | |
class Values[A](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 { | |
/** | |
* @return All the possible values of `A` | |
*/ | |
@inline def apply[A](implicit values: Values[A]): Values[A] = values | |
/** | |
* 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 derived[A, Repr <: Coproduct](implicit gen: Generic.Aux[A, Repr], v: ValuesAux[A, Repr]): Values[A] = | |
new Values[A](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) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment