Created
June 5, 2015 14:32
-
-
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 ...
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
// 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
How many of the points on this StackOverflow answer does you solution provide?
http://stackoverflow.com/a/25923651/501113