Many students in my Scala classes expect that
Tuples will behave like collections by providing methods like
tail, or providing access to elements by an index like
Seq types provide. It's a reasonable expectation, but unfortunately they don't do this. By default we're limited to accessing the elements through methods like
_2, and have none of the handy features of
Seq. We can make this situation better with the addition of the shapeless library.
Tuple has a type annotation for each element. A
Tuple with a
Int is typed as
Tuple2[String, Int]. The first element in this
Tuple2 must be a
String while the second must be an
Int. Shapeless provides a type called
HList, which is a collection of heterogenous elements where each element in the collection has it's own type. Contrast this to the standard Scala collections where every element in the collection must be the same type. Conceptually, an
HList with a
Int could be thought of as
HList[String, Int]. This is defining a collection that has two elements with a
String first and an
Int second. Does this sound kind of like a
Tuple2? Shapeless provides a little syntax sugar that makes a standard
Tuple behave like a collection.
Let's take a look at this in action! As usual, I will use sbt and the REPL to interactively explore Shapless. Launch sbt (or activator) and enter the following commands to bring in the library:
> set scalaVersion := "2.11.7" > set libraryDependencies += "com.chuusai" %% "shapeless" % "2.2.4"
Then launch the Scala REPL with the console command:
> console [info] Starting scala interpreter... [info] Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25). Type in expressions to have them evaluated. Type :help for more information. scala>
First, import the packages that we need:
scala> import shapeless._ import shapeless._ scala> import syntax.std.tuple._ import syntax.std.tuple._
The last import brings in the helpers for tuples. Let's create a
Tuple to test with.
scala> val t1 = ("Scala", 1, true) t1: (String, Int, Boolean) = (Scala,1,true)
This is a standard library
Tuple3[String, Int, Boolean]. But with Shapeless we now have head and tail:
scala> t1.head res2: String = Scala scala> t1.tail res3: (Int, Boolean) = (1,true)
We can access elements with the
apply method just like
scala> t1(0) res6: String = Scala scala> t1(1) res7: Int = 1
And we can concatenate tuples with the usual operators:
scala> t1 :+ "yay" res11: (String, Int, Boolean, String) = (Scala,1,true,yay) scala> t1 ++ ("yay", 5.0) res12: (String, Int, Boolean, String, Double) = (Scala,1,true,yay,5.0)
Making Tuples act like collections is just the tip of the Shapeless iceberg, but it's definitely a nice feature!