Skip to content

Instantly share code, notes, and snippets.

@milessabin
Created June 5, 2015 14:32
Show Gist options
  • Save milessabin/25b7b669b5a9ac051a71 to your computer and use it in GitHub Desktop.
Save milessabin/25b7b669b5a9ac051a71 to your computer and use it in GitHub Desktop.
A safe ADT+shapeless drop-in replacement for the unsafe standard Scala Enumeration ...
// 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 }
}
}
}
@chaotic3quilibrium
Copy link

How many of the points on this StackOverflow answer does you solution provide?
http://stackoverflow.com/a/25923651/501113

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment