NOTE: This project is no longer actively maintained.
A small library (<300 loc) that provides macros and typeclasses for enums encoded as sealed trait
hierarchies.
Part of rbricks, a collection of composable, small-footprint libraries for scala.
- (Optional) macro annotations for compact "enum" syntax.
import io.rbricks.itemized.annotation.enum
@enum trait Planet {
object Earth
object Venus
object Mercury
}
This expands to:
sealed trait Planet extends io.rbricks.itemized.Itemized
object Planet {
case object Earth extends Planet
case object Venus extends Planet
case object Mercury extends Planet
}
- Typeclass instances to convert to and from
String
are automatically derived:
scala> import io.rbricks.itemized.ItemizedCodec
scala> ItemizedCodec[Planet].fromRep("Earth")
res0: Option[Planet] = Some(Earth)
scala> val planet: Planet = Planet.Earth
scala> import io.rbricks.itemized.ItemizedCodec.ops._
scala> planet.toRep
- Pattern matching against the sealed hierarchy supports exhaustiveness checking, for added safety.
scala> (Planet.Earth : Planet) match {
| case Planet.Earth => "close"
| case Planet.Venus => "far"
| }
<console>:19: warning: match may not be exhaustive.
It would fail on the following input: Mercury
(Planet.Earth : Planet) match {
^
res1: String = close
- Support for serialization libraries, currently (click on links for usage/installation instructions):
Artifacts for Scala 2.11 and 2.12 are available on Maven Central.
Add the dependency to your build.sbt
libraryDependencies += "io.rbricks" %% "itemized" % "0.2.0"
To enable the macro paradise plugin (for the @enum
annotation), also add
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
The @enum
annotation builds enumerations that follow the library's convention for how ADT-based enums should be encoded. However, usage of the macro annotation can be avoided by manually writing out the ADT. Here's an example that serves as an informal definition of the convention.
sealed trait Planet extends io.rbricks.itemized.Itemized
object Planet {
case object Earth extends Planet
case object Venus extends Planet
case object Mercury extends Planet
}
This is equivalent to the following (which expands to the same encoding).
@enum trait Planet {
object Earth
object Venus
object Mercury
}
Usage of the @enum
macro annotations requires the macro paradise plugin to be enabled in your project. Refer to the Install section for how to set it up.
The ItemizedCodec
typeclass provides operations to convert ADT-based enumerations to and from Strings.
Implemetors of encoding and decoding (serialization) protocols may use it as follows:
implicit def itemizedJsonEncoding[T <: Itemized](implicit instance: ItemizedCodec[T]) = new JsonEncoding[T] {
def write(value: T): JsonObject = JsonString(instance.caseToString(value))
def read(jsonObject: JsonObject) = ... instance.itemizedFromString(str).get
}
Additionally, ItemizedCodec[T]
exposes the Map[String, T]
as .stringMap
, for other use-cases (such as retrieving the set of admissible elements or strings).
The @indexedEnum
annotation builds enumerations that follow the library's convention for ADT-based enums with an associated value.
sealed trait Planet extends io.rbricks.itemized.IndexedEnum {
type Index = Int
}
object Planet {
case object Earth extends Planet { val index = 1 }
case object Venus extends Planet { val index = 2 }
case object Mercury extends Planet { val index = 3 }
}
This is equivalent to the following.
import io.rbricks.itemized.annotation.indexedEnum
@indexedEnum trait Planet {
type Index = Int
object Earth { 1 }
object Venus { 2 }
object Mercury { 3 }
}
Usage of the @indexedEnum
macro annotations requires the macro paradise plugin to be enabled in your project. Refer to the Install section for how to set it up.
The ItemizedIndex
typeclass provides operations to convert ADT-based enums to and from their associated values.
import io.rbricks.itemized.annotation.indexedEnum
@indexedEnum trait Planet {
type Index = Int
object Earth { 1 }
object Venus { 2 }
object Mercury { 3 }
}
Examples of usage follow.
scala> import io.rbricks.itemized.ItemizedIndex
scala> ItemizedIndex[Planet].fromIndex(2)
res0: Option[Planet] = Some(Venus)
scala> Planet.Mercury.index
res1: Int = 3
scala> import io.rbricks.itemized.ItemizedIndex.ops._
scala> val planet: Planet = Planet.Mercury
scala> planet.toIndex
res2: Planet#Index = 3
Additionally, ItemizedIndex[T]
exposes the Map[T#Index, T]
as .indexMap
, for other use-cases (such as retrieving the set of admissible elements or indices).
- circe encoders and decoders (@utaal, 6ac3e9a)
- More info in macro errors (@esarbe, 0de55b3)
ItemizedCodec.stringMap
andItemizedIndex.indexMap
retrieve the underlying string<->object or index<->object mappings (@esarbe, 25013c4)