Skip to content

Instantly share code, notes, and snippets.

@bigtoast
Forked from milessabin/gist:25b7b669b5a9ac051a71
Last active August 29, 2015 14:23
Show Gist options
  • Save bigtoast/cd693932cb8b14324be4 to your computer and use it in GitHub Desktop.
Save bigtoast/cd693932cb8b14324be4 to your computer and use it in GitHub Desktop.
// An ADT+shapeless as a drop-in replacement for a standard Scala Enumeration.
//
// First the unsafe standard Scala Enumeration ...
//
object ScalaEnumDemo extends App {
// Example from scala.Enumeration scaladoc. Terse ...
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
assert((WeekDay.values filter isWorkingDay) == Set(Mon, Tue, Wed, Thu, Fri))
// However ...
def isWeekend(d: WeekDay) = d match {
case Sat | Sun => true
// Oops! Missing case ... still compiles
}
assert(!isWeekend(Mon)) // MatchError at run time
}
// A safer ADT+shapeless alternative ...
//
object ShapelessEnumDemo extends App {
// ADT as an enumeration. A little more boilerplate ...
sealed trait WeekDay
object WeekDay {
case object Mon extends WeekDay
case object Tue extends WeekDay
case object Wed extends WeekDay
case object Thu extends WeekDay
case object Fri extends WeekDay
case object Sat extends WeekDay
case object Sun extends WeekDay
val values: Set[WeekDay] = Values
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
assert((WeekDay.values filter isWorkingDay) == Set(Mon, Tue, Wed, Thu, Fri))
// ... the payoff ...
def isWeekend(d: WeekDay) = d match {
case Sat | Sun => true
case _ => false // compile time non-exhaustive match warning/error without this case
}
assert(!isWeekend(Mon)) //
}
// Infrastructure for the above. Original version due to Travis Brown,
//
// http://stackoverflow.com/questions/25838411
//
object Values {
implicit def conv[T](self: this.type)(implicit v: MkValues[T]): Set[T] = Values[T]
def apply[T](implicit v: MkValues[T]): Set[T] = v.values.toSet
trait MkValues[T] {
def values: List[T]
}
object MkValues {
import shapeless._
implicit def values[T, Repr <: Coproduct]
(implicit gen: Generic.Aux[T, Repr], v: Aux[T, Repr]): MkValues[T] =
new MkValues[T] { def values = v.values }
trait Aux[T, Repr] {
def values: List[T]
}
object Aux {
implicit def cnilAux[A]: Aux[A, CNil] =
new Aux[A, CNil] { def values = Nil }
implicit def cconsAux[T, L <: T, R <: Coproduct]
(implicit l: Witness.Aux[L], r: Aux[T, R]): Aux[T, L :+: R] =
new Aux[T, L :+: R] { def values = l.value :: r.values }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment