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:
s
cala>
val l
ist
= 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 h
List
= 10 :: "
anyString
" :: 1.0 ::
H
Nil
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 🙂
Reblogged this on deeptibhblog.
Reblogged this on himaniarora1.
Reblogged this on Prabhat Kashyap – Scala-Trek.
Reblogged this on kunalkapoor.
variable name `polyListToSet ` is a bit confusing. Probable should be renamed to `polyOptionToList`.
Thanks for the article.
Thanks for pointing it out 🙂