shapeless 1.0
Selected highlights of this 1.0 release of shapeless include,
- A new encoding of polymorphic function values which optionally supports type specific cases, and which is interoperable with Scala's ordinary monomorphic function values.
```scala // choose is a function from Sets to Options with no type specific cases object choose extends (Set ~> Option) { def default[T](s : Set[T]) = s.headOption }
// choose is convertible to an ordinary monomorphic function value
val lo = List(Set(1, 3, 5), Set(2, 4, 6)) map choose
lo == List(Option(1), Option(2))
// size is a function from values of arbitrary type to a 'size' which is
// defined via type specific cases
object size extends (Id ~> Const[Int]#λ) {
def default[T](t : T) = 1
}
implicit def sizeInt = size.λ[Int](x => 1)
implicit def sizeString = size.λ[String](s => s.length)
implicit def sizeList[T] = size.λ[List[T]](l => l.length)
implicit def sizeOption[T](implicit cases : size.λ[T]) =
size.λ[Option[T]](t => 1+size(t.get))
implicit def sizeTuple[T, U](implicit st : size.λ[T], su : size.λ[U]) =
size.λ[(T, U)](t => size(t._1)+size(t._2))
size(23) == 1
size("foo") == 3
size((23, "foo")) == 4```
- An implementation of Scrap your Boilerplate with Class which provides generic map and fold operations over arbitrarily nested data structures,
```scala val nested = List(Option(List(Option(List(Option(23))))))
val succ = everywhere(inc)(nested) succ == List(Option(List(Option(List(Option(24))))))
```
- A
Typeabletype class which provides a type safe cast operation.
```scala val a : Any = List(Vector("foo", "bar", "baz"), Vector("wibble"))
val lvs : Option[List[Vector[String]]] = a.cast[List[Vector[String]]] lvs.isDefined == true val lvi : Option[List[Vector[Int]]] = a.cast[List[Vector[Int]]] lvi.isEmpty == true
```
- The mother of all Scala
HList's, which amongst other things, - is covariant.
```scala trait Fruit case class Apple() extends Fruit case class Pear() extends Fruit
type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil type APAP = Apple :: Pear :: Apple :: Pear :: HNil val a : Apple = Apple() val p : Pear = Pear() val apap : APAP = a :: p :: a :: p :: HNil val ffff : FFFF = apap // APAP <: FFFF
```
- has a map operation, applying a polymorphic function value (possibly
with type specific cases) across its elements. This means that it
subsumes both typical
HList's and alsoKList's (HList's whose elements share a common outer type constructor).
```scala type SISS = Set[Int] :: Set[String] :: HNil type OIOS = Option[Int] :: Option[String] :: HNil
val sets : SISS = Set(1) :: Set("foo") :: HNil
val opts : OIOS = sets map choose
opts == Option(1) :: Option("foo") :: HNil```
- has a zipper for traversal and persistent update.
```scala val l = 1 :: "foo" :: 3.0 :: HNil
val l2 = l.toZipper.right.put("wibble", 45).toHList
l2 == 1 :: ("wibble", 45) :: 3.0 :: HNil
val l3 = l.toZipper.right.delete.toHList
l3 == 1 :: 3.0 :: HNil
val l4 = l.toZipper.last.left.insert("bar").toHList
l4 == 1 :: "foo" :: "bar" :: 3.0 :: HNil, l5)```
- has a
unifyoperation which converts it to anHListof elements of the least upper bound of the original types.
`scala
val ffff = apap.unify // type inferred as FFFF
`
- supports conversion to an ordinary Scala
Listof elements of the least upper bound of the original types.
`scala
val lf = apap.toList // type inferred as List[Fruit]
lf == List(a, p, a, p)
`
- has a
Typeabletype class instance, allowing, eg. vanillaList[Any]'s orHList's with elements of typeAnyto be safely cast to precisely typedHList's.
```scala
val ffff : FFFF = apap.unify // discard precise typing
val apap2 : Option[APAP] = ffff.cast[APAP] // reestablish precise typing
apap2.get == apap
```
These last three bullets make this HList dramatically more practically
useful than HList's are typically thought to be: normally the full
type information required to work with them is too fragile to cross subtyping
or I/O boundaries. This implementation supports the discarding of precise
information where necessary (eg. to serialize a precisely typed record after
construction), and its later reconstruction (eg. a weakly typed deserialized
record with a known schema can have it's precise typing reestabilished).
- Conversions between tuples and
HList's, and between ordinary Scala functions of arbitrary arity and functions which take a single correspondingHListargument. One application of this is theliftOfunction which lifts an ordinary function of arbitrary arity intoOption.
```scala // Round trip from tuple to HList and back val t1 = (23, "foo", 2.0, true)
val l1 = t1.hlisted h1 == 23 :: "foo" :: 2.0 :: true :: HNil val t2 = l1.tupled t1 == t2 // Lift these ordinary Scala function values into Option val sum : (Int, Int) => Int = _ + _ val prd : (Int, Int, Int) => Int = _ * _ * _ // Nb. liftO abstracts over the arity of its function arguments val sumO = liftO(sum) // (Option[Int], Option[Int]) => Option[Int] val prdO = liftO(prd) // (Option[Int], Option[Int], Option[Int]) => Option[Int] val s1 = sumO(Some(1), Some(2)) s1 == Option(3) val s2 = sumO(Some(1), None) s2 == None val p1 = prdO(Some(2), Some(3), Some(4)) p1 == Option(24) val p2 = prdO(Some(2), None, Some(4)) p2 == None
```
