Conversions between tuples and HList
's, and between ordinary Scala functions of arbitrary arity and functions which
take a single corresponding HList
argument allow higher order functions to abstract over the arity of the functions
and values they are passed
Based on and extending Generic
and LabelledGeneric
, Lars Hupel (@larsr_h) has contributed the TypeClass
family of type classes, which provide automatic type class derivation facilities roughly equivalent to those available
with GHC as described in "A Generic Deriving Mechanism for Haskell".
Based on and extending Generic
and LabelledGeneric
, Lars Hupel (@larsr_h) has contributed the TypeClass
family of type classes, which provide automatic type class derivation facilities roughly equivalent to those available
with GHC as described in "A Generic Deriving Mechanism for Haskell". There is a description of an
earlier iteration of the Scala mechanism here, and examples of its use deriving Show
and Monoid
instances here
and here for labelled coproducts and unlabelled products respectively.
For example, in the Monoid
case, once the general deriving infrastructure for monoids is in place, instances are
automatically available for arbitrary case classes without any additional boilerplate
trait Monoid[T] { def zero: T def append(a: T, b: T): T } object Monoid extends ProductTypeClassCompanion[Monoid] { def mzero[T](implicit mt: Monoid[T]) = mt.zero implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] { def zero = false def append(a: Boolean, b: Boolean) = a || b } implicit def intMonoid: Monoid[Int] = new Monoid[Int] { def zero = 0 def append(a: Int, b: Int) = a+b } implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] { def zero = 0.0 def append(a: Double, b: Double) = a+b } implicit def stringMonoid: Monoid[String] = new Monoid[String] { def zero = "" def append(a: String, b: String) = a+b } object typeClass extends ProductTypeClass[Monoid] { def emptyProduct = new Monoid[HNil] { def zero = HNil def append(a: HNil, b: HNil) = HNil } def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] { def zero = mh.zero :: mt.zero def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail) } def project[F, G](instance: => Monoid[G], to: F => G, from: G => F) = new Monoid[F] { def zero = from(instance.zero) def append(a: F, b: F) = from(instance.append(to(a), to(b))) } } } trait MonoidSyntax[T] { def |+|(b: T): T } object MonoidSyntax { implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] { def |+|(b: T) = mt.append(a, b) } }
The shapeless-contrib project also contains automatically derived type class instances for Scalaz, Spire and Scalacheck.
shapeless has a Coproduct type, a generalization of Scala's Either
to an arbitrary number of choices.
shapeless has a Coproduct type, a generalization of Scala's Either
to an arbitrary number of choices. Currently it
exists primarily to support Generic
(see the next section), but will be expanded analogously to HList
in later
releases. Currently Coproduct
supports mapping, selection and unification
shapeless provides an implementation of extensible records modelled as HLists
of values tagged with the singleton
types of their keys.
shapeless provides an implementation of extensible records modelled as HLists
of values tagged with the singleton
types of their keys. This means that there is no concrete representation needed at all for the keys. Amongst other
things this will allow subsequent work on Generic
to map case classes directly to records with their member names
encoded in their element types.
import shapeless._ ; import syntax.singleton._ ; import record._ val book = ("author" ->> "Benjamin Pierce") :: ("title" ->> "Types and Programming Languages") :: ("id" ->> 262162091) :: ("price" ->> 44.11) :: HNil
The Iso
s of earlier shapeless releases have been completely reworked as the new Generic
type, which closely
resembles the generic programming capabilities introduced to GHC 7.2.
The Iso
s of earlier shapeless releases have been completely reworked as the new Generic
type, which closely
resembles the generic programming capabilities introduced to GHC 7.2.
Generic[T]
, where T
is a case class or an abstract type at the root of a case class hierarchy, maps between values
of T
and a generic sum of products representation (HList
s and Coproduct
s),
shapeless provides a comprehensive Scala HList
which has many features not shared by other HList implementations.
shapeless provides a comprehensive Scala HList
which has many features not shared by other HList implementations.
Shapeless provides a heterogenous map which supports an arbitrary relation between the key type and the corresponding value type,
Shapeless provides a heterogenous map which supports an arbitrary relation between the key type and the corresponding value type,
class BiMapIS[K, V] implicit val intToString = new BiMapIS[Int, String] implicit val stringToInt = new BiMapIS[String, Int] val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13) //val hm2 = HMap[BiMapIS](23 -> "foo", 23 -> 13) // Does not compile
Traversals and transformations of recursive types (eg.
Traversals and transformations of recursive types (eg. cons lists or trees) must themselves be recursive. Consequently type class instances which perform such operations must be recursive values in turn. This is problematic in Scala at the both the value and the type levels: at the value level the issue is that recursive type class instances would have to be constructed lazily, whilst Scala doesn't natively support lazy implicit arguments; at the type level the issue is that during the type checking of expressions constructing recursive implicit values the implicit resolution mechanism would revisit types in a way that would trip the divergence checker.
The Lazy[T]
type constructor and associated macro in shapeless addresses both of these problems in many cases. It is
similar to Scalaz's Need[T]
and adds lazy implicit construction and suppression of divergence checking. This
supports constructions such as...
A combination of LabelledGeneric
and singleton-typed Symbol
literals supports boilerplate-free lens creation for
arbitrary case classes
A combination of LabelledGeneric
and singleton-typed Symbol
literals supports boilerplate-free lens creation for
arbitrary case classes
// A pair of ordinary case classes ... case class Address(street : String, city : String, postcode : String) case class Person(name : String, age : Int, address : Address) // Some lenses over Person/Address ... val nameLens = lens[Person] >> 'name val ageLens = lens[Person] >> 'age val addressLens = lens[Person] >> 'address val streetLens = lens[Person] >> 'address >> 'street val cityLens = lens[Person] >> 'address >> 'city val postcodeLens = lens[Person] >> 'address >> 'postcode val person = Person("Joe Grey", 37, Address("Southover Street", "Brighton", "BN2 9UA"))
Ordinary Scala function values are monomorphic.
Ordinary Scala function values are monomorphic. shapeless, however, provides an encoding of polymorphic function values. It supports natural transformations, which are familiar from libraries like Scalaz
Shapeless is a type class and dependent type based generic programming library for Scala.
Although Scala's typechecker has always represented singleton types for literal values internally, there has not previously been syntax available to express them, other than by [modifying the compiler][literaltype].
Although Scala's typechecker has always represented singleton types for literal values internally, there has not previously been syntax available to express them, other than by [modifying the compiler][literaltype]. shapeless adds support for singleton-typed literals via implicit macros.
shapeless provides collection types with statically known sizes.
shapeless provides collection types with statically known sizes. These can prevent runtime errors such as those that would result from attempting to take the head of an empty list, and can also verify more complex relationships.
def row(cols : Seq[String]) = cols.mkString("\\"", "\\", \\"", "\\"") def csv[N <: Nat] (hdrs : Sized[Seq[String], N], rows : List[Sized[Seq[String], N]]) = row(hdrs) :: rows.map(row(_)) val hdrs = Sized("Title", "Author") val rows = List( Sized("Types and Programming Languages", "Benjamin Pierce"), Sized("The Implementation of Functional Programming Languages", "Simon Peyton-Jones") )
shapeless allows standard Scala tuples to be manipulated in exactly the same ways as HList
s
shapeless allows standard Scala tuples to be manipulated in exactly the same ways as HList
s
Libraries like shapeless which make extensive use of type-level computation and implicit resolution often need to provide guarantees that certain expressions *don't* typecheck.
Libraries like shapeless which make extensive use of type-level computation and implicit resolution often need to
provide guarantees that certain expressions *don't* typecheck. Testing these guarantees is supported in shapeless via
the illTyped
macro,
import shapeless.test.illTyped scala> illTyped { """1+1 : Boolean""" } scala> illTyped { """1+1 : Int""" } <console>:19: error: Type-checking succeeded unexpectedly. Expected some error. illTyped { """1+1 : Int""" }
shapeless provides a Typeable
type class which provides a type safe cast operation.
shapeless provides a Typeable
type class which provides a type safe cast operation. cast
returns an Option
of the
target type rather than throwing an exception if the value is of the incorrect type, as can happen with separate
isInstanceOf
and asInstanceOf
operations. Typeable
handles primitive values correctly and will recover erased
types in many circumstances
Facilities for abstracting over arity
Conversions between tuples and
HList
's, and between ordinary Scala functions of arbitrary arity and functions which take a single correspondingHList
argument allow higher order functions to abstract over the arity of the functions and values they are passed