utforsca is a collection of utils that I have personally deemed incredibly useful for scala. The project follows these principles in regards to what gets added
- Small: Utils that are added are small, utforsca isn't designed to be a kitchen sink library that implements every single use case. Utils are only added if they are deemed as necessary (often because they are not possible in Scala )
- Clean and Simple: The Utils that are added are designed to be very simple, clean and easy to use. In other words, the utils are designed to leverage scala as much as possible. utforsca is not designed to be scalaz, the utils being added aren't meant to be as generic as some other libraries (possibly being restrictive on purpose), they are designed to be easy to pick up and use
- Easy to maintain: Because of the the previous two points, utforsca is designed to be easy/trivial to maintain. API changes occurrences are to happen as minimal as possible (ideally never)
Artifacts are published to Sonatype, so you shouldn't need to add any repositories
Simply add the following dependency to libraryDependencies
, for Scala 2.11.x
"org.mdedetrich" %% "utforsca" % "2.2.0"
Or for Scala 2.10.x
"org.mdedetrich" %% "utforsca" % "1.2.0"
Here is a list of the utils contained within utforsca
import org.mdedetrich.utforsca.SealedContents
SealedContents
is an incredibly handy macro taken from here.
The macro automatically constructs a Set[T]
of an ADT T
, that contains an enumeration of all the types
contained within T
. As a very simple example, assume that we create some ADT Title
, which contains various Titles that can be attributed to a person
sealed abstract class Title(val id:Long val formalName:String)
Lets now populate our Title
with various entries
case object Mr extends Title(1,"Mr")
case object Mrs extends Title(2,"Mrs")
case object Miss extends Title(3,"Miss")
case object Ms extends Title(4,"Ms")
case object Dr extends Title(5,"Dr")
case object Professor extends Title(6,"Professor")
Now lets create a companion object which holds our sealed contents
object Title {
val all:Set[Title] = SealedContents.values[Title]
}
Title.all
will now contain an enumeration of all of the child values of Title, which lets us do stuff like this
// Find a title by id
Title.all.find(_.id == id)
// Enumerate through every title to get its id
Title.all.map(_.id)
Very obvious use case for this is allowing to represent enumerated types easily in a database. If you are using something
like Slick, you can easily create mappers going from, and to, type Title
. Its also a very non verbose way to define static
databases in your code. Its a very nice, type safe replacement for Java/Scala's Enum
type
Limitations exist for SealedContents
, however they are quite obvious. The type T
that you run SealedContents.value[T]
on, must
- Be
sealed
: The compiler needs to now all possible child values at compile time to generate enumeration - Every child must be an
case object
, not acase class
:case object
s can be instantiated without a constructor, so its actually possible to create instances of these types at runtime
import org.mdedetrich.utforsca.AsMap._
asMap
is macro taken from here which converts a case class to a map.
In other words, if you have a case class like the following
case class SomeCaseClass(string:String,int:Int)
val someCaseClass = SomeCaseClass("rawr",5)
asMap
will convert the case class to a map
val map = someCaseClass.asMap
println(map)
Will print the following
Map(
"string" -> "rawr",
"rawr" -> 5
)
asMap
will also work for existential types, i.e. if you are using something like Spire and do the following
case class SomeNumericCaseClass[T : Numeric](someNumeric:T,listOfSomeNumeric:List[T])
Check the tests for more info
asMap will often return a map of type Map[String,Any]
, this means you will probably have to match against the value to check for its type.
In other words, you will probably end up doing something like this
val map = someCaseClass.asMap
map.foreach{case(key,value) =>
value match {
case s:String => println(s"key:$key contains a string:$s")
case i:Int => println(s"key:$key contains an int:$i")
}
}