74

Can someone explain to me in simple terms what the Shapeless library is for?

Scala has generics and inheritance functionality so I'm a bit confused what Shapeless is for.

Maybe a use case to clarify things would be helpful.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
loyalflow
  • 14,275
  • 27
  • 107
  • 168

3 Answers3

53

It's a little hard to explain, as shapeless has a wide range of features; I'd probably find it easier to "explain, in simple terms, what variables are for". You definitely want to start with the feature overview.

Broadly speaking, shapeless is about programming with types. Doing things at compile-time that would more commonly be done at runtime, keeping precise track of the type of each element in a list, being able to translate from tuples to HLists to case classes, creating polymorphic functions (as opposed to methods), etc.

A typical usage scenario would go something like:

  • Read a bunch of values from somewhere into a List
  • perform a type-safe cast of that List into an HList
  • map over that HList with a polymorphic function that e.g. normalises values
  • convert the 3rd element (which is statically known to be an Int) into a 0-padded string
  • construct a case class using values from the HList

For reference, an HList will have a precise type, such as Int :: String :: Boolean :: HNil (yes, that really is a single type) where everything is pinned down and the size is fixed. So you either need to know at compile time exactly what will be going into your HList, or you need the type-safe cast.

If you take the tail of such an HList, you get a String :: Boolean :: HNil, and a compile-time guarantee that the head of this will be a String. Prepending a value to the head will similarly preserve all types involved.

Shapeless also comes with the Generic type class, allowing you to use HList operations on tuples and case classes as well.

The other features I tend to use are:

  • Coproducts, which allow you to statically type a value as being e.g. "a String, Double or Int, but nothing else" (much like Either, but not limited to just two possibilities)

  • Lenses, which simplify working with nested case classes.

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
33

Looking at an HList is something that might seem baffling until you try to work with types and delegate or switch on types. Take a look at the following:

val myList = 1 :: 2 :: "3" :: fred :: Nil

What is the type of myList here? If you were to inspect it, you'd see it was of type List[Any]. That's not very helpful. What's even less helpful is if I tried to use the following PartialFunction[Any] to map over it:

myList.map{
  case x: Int => x
  case x: String => Int.parseInt(x)
}

At runtime, this might throw a MatchError because I haven't actually told you what type fred is. It could be of type Fred.

With an HList you can know right at compile time if you've failed to capture one of the types of that list. In the above, if I had defined myList = 1 :: 2 :: "3" :: fred :: HNil when I accessed the 3rd element, it's type would be String and this would be known at compile time.

As @KevinWright states, there's more to it than that with Shapeless but HList is one of the defining features of the library.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
wheaties
  • 35,646
  • 15
  • 94
  • 131
21

Everything in Shapeless has two things in common:

First, it isn't in the Scala standard library, but arguably should be. Therefore, asking what Shapeless is for is a bit like asking with the Scala standard library is for! It's for everything. It's a grab bag.

(but it isn't a totally arbitrary grab bag, because:)

Second, everything in Shapeless provides increased checking and safety at compile time. Nothing in Shapeless (that I can think of?) actually “does” anything at runtime. All of the interesting action happens when your code is compiled. The goal is always increased confidence that if your code compiles at all, it won't crash or do the wrong thing at runtime. (Hence this notable quip: https://twitter.com/mergeconflict/status/304090286659866624 "Does Miles Sabin even exist at runtime?")

There is a nice introduction to what type-level programming is all about, with links to further resources, at https://stackoverflow.com/a/4443972/86485.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
  • 3
    "First, it isn't in the Scala standard library, but arguably should be." If this would ever happen I would run away... and scream. – JayZee Aug 01 '16 at 10:05
  • @JayZee why do you say that? – stsatlantis Mar 08 '17 at 11:51
  • 1
    let me try a comparison...if scala type safety is vegetarianism shapeless is veganism! It adds an extra layer of type safety that, in my opinion, complicate the already complicated life of scalac compiler. Shapeless requires more implicits, and more type classes. I'm not a super expert of scala compilation but the price you pay with shapeless is that the compiler will have harder times with shapeless around (at least is my experience) and will slow down. I prefer to have a bunch of "isInstanceOf" , hit sometimes ClassCastException and fix instead of relying on the magics under shapeless. – JayZee Mar 08 '17 at 15:47
  • Consider also that even your IDE will have an harder time to type infer things or try to compile your code, even if this may not be an issue for you. – JayZee Mar 08 '17 at 16:02
  • Accent on the “arguably”. – Seth Tisue May 15 '17 at 22:06