Introduction to Shapeless !


Shapeless is a type class and dependent type based generic programming library for Scala. It is an Open Source project under the Apache License v2, hosted on github.

Well, simply put, it is a well known library for generic programming in scala.

Earlier, reflection APIs were used to write generic programs. However, since reflection is usually done in runtime, it sacrifices type-safety, and introduces runtime failures.

But luckily, Shapeless is there to solve problems during compilation where they would normally be tackled in runtime. Shapeless aims to give you confidence that if a piece of code compiles, it will run as well.

Using Shapeless:

To include the Shapeless in your SBT project for scala 2.11.8 you should add the following in your SBT build,

libraryDependencies ++= Seq(
  "com.chuusai" %% "shapeless" % "2.3.1"
)

Shapeless has a wide range of features. Lets have a look at two of them where Scala did not join hands with us.

  • Polymorphic function values

    Before that, lets understand what a monomorphic and a polymorphic function is:

    Monomorphic methods can only be applied to arguments of the fixed types specified in their signatures and their subtypes. For eg.

    def findSize(s: String): Int = s.length

    However, polymorphic methods can be applied to arguments of any types which correspond to acceptable choices for their type parameters. For eg.

    def findSize[T](l: List[T]): Int = l.length

    Scala allows both monomorphic as well as the polymorphic methods. The real problem arises with function values.

    In Scala, we cannot achieve polymorphic function values and therefore it produces some lack of expressiveness. Try assigning function to a val(Eta expansion):

    scala> def monomorphicListToSet(l: List[Int]): Set[Int]= l.toSet
    monomorphicListToSet: (l: List[Int])Set[Int]
    
    scala> val a = monomorphicListToSet _
    a: List[Int] => Set[Int] = <function1>

    Have a look at the definition of the above function that transforms the List[Int] into a Set[Int]. The type Int is restricted here. But Scala syntax doesn’t allow to define something similar for polymorphic functions

    scala> def polymorphicListToSet[T](l: List[T]): Set[T] = l.toSet
    
    polymorphicListToSet: [T](l: List[T])Set[T]
    
    scala> val sadlyMonomorphic = polymorphicListToSet _
    
    sadlyMonomorphic: List[Nothing] => Set[Nothing] = <function1>

    this is where the compiler goes wrong, it says the list contained type is Nothing.

    Here Shapeless comes to our rescue.

    It provides an encoding of polymorphic function values. It supports natural transformations, First of them has the following notation:

    import shapeless.poly._
    scala> val polyOptionToList = new (Option ~> List){
         | def apply[T](f: Option[T]): List[T]= 
         | f.toList
         | }
    polyOptionToList: shapeless.poly.~>[Option,List] = $anon$1@1e731f50
    
    
    scala> val result = 
    polyOptionToList(Option(2)) result: List[Int] = List(2)

    The other possible notation consists of defining the function behavior based on cases, where in we can define the function only for Int, String and Boolean by adding a case for each data type or as the need arises.

    import shapeless.Poly1
    scala>object size extends Poly1 {
         |   implicit def caseInt = at[Int](x ⇒ 1)
         |   implicit def caseString = at[String](_.length)
         |   implicit def caseTuple[T, U](implicit st: Case.Aux[T, Int], su: Case.Aux[U, Int]) =
         |     at[(T, U)](t ⇒ size(t._1) + size(t._2))
         | }
    defined object size
    
    scala> size(23)
    res0: Int = 1
    scala> size("foobar")
    res1: Int = 6
    scala> size((23, "foobar"))
    res2: Int = 7
    scala> size(((23, "foobar"), 13))
    res3: Int = 8

    Another promising feature of Shapeless is the support for HLists.

  • HLists (short for Heterogeneous Lists) are lists of objects of arbitrary types, where the type information for each object is kept. In fact, in Scala, we may do:

    scala>val list = 10 :: "anyString" :: 1.0 :: Nil
    list: List[Any] = List(10, anyString, 1.0)

    as you can see the type of list is List[Any], beacuse the common supertype of all the elements is Any. However an HList is declared in the same way:

scala>val hList = 10 :: "anyString" :: 1.0 :: HNil
hList: shapeless.::[Int,shapeless.::[String,shapeless.:[Double,shapeless.HNil]]] = 10 :: anyString :: 1.0 :: HNil

except for the terminator HNil. But the type of hList is actually Int::String::Double::HNil

An HList stores the type of every element in the list. This way we know the type of the first element, the type of the second element, and so forth. As you can see, no type information is lost.

Well you might wonder, why Hlists over Lists ??

lets have an example to prove it.

scala> list.tail.head.toUpperCase 

// error: value toUpperCase is not a member of Any

scala> hList.tail.head.toUpperCase

res6: String = ANYSTRING

Why should you really use HLists?

  • HLists can be used in all conditions where a tuple would work, but without the 22- element limitation.
  • Also the above two are convertible to each other via ‘tupled’ and ‘productElements’ methods.

For other promising features of Shapeless, you can have a look at the below link:

https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0

Happy Reading 🙂

 

 

This entry was posted in Scala and tagged , , . Bookmark the permalink.

6 Responses to Introduction to Shapeless !

  1. Prabhat Kashyap says:

    Reblogged this on Prabhat Kashyap – Scala-Trek.

  2. variable name `polyListToSet ` is a bit confusing. Probable should be renamed to `polyOptionToList`.
    Thanks for the article.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s