or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

conversions.mdgeneric.mdhlist.mdhmap.mdindex.mdlift.mdnat.mdpoly.mdrecords.mdsized.mdsybclass.mdtypeable.mdtypeoperators.md
tile.json

tessl/maven-com-chuusai--shapeless-2-11

An exploration of generic (aka polytypic) programming in Scala derived from implementing scrap your boilerplate and higher rank polymorphism patterns

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.chuusai/shapeless_2.11@1.2.x

To install, run

npx @tessl/cli install tessl/maven-com-chuusai--shapeless-2-11@1.2.0

index.mddocs/

Shapeless

Shapeless is a type class and dependent type based generic programming library for Scala. It provides powerful abstractions for working with heterogeneous data structures and enables type-safe generic programming through compile-time type manipulation and automatic type class derivation.

Package Information

  • Package Name: shapeless
  • Package Type: Scala library
  • Version: 1.2.4
  • Language: Scala
  • Installation: Add to your build.sbt:
libraryDependencies += "com.chuusai" %% "shapeless" % "1.2.4"

Core Imports

import shapeless._

For specific functionality, use targeted imports:

import shapeless.HList._
import shapeless.Poly._
import shapeless.Nat._
import shapeless.record._
import shapeless.syntax.sized._

Basic Usage

import shapeless._

// Create heterogeneous lists
val hlist = 23 :: "foo" :: true :: HNil
val head: Int = hlist.head
val tail = hlist.tail

// Type-safe record operations
val book = ("author" ->> "Benjamin Pierce") :: ("title" ->> "TAPL") :: ("id" ->> 991) :: HNil
val author: String = book("author")
val updated = book.updated("price", 89.95)

// Polymorphic functions
object size extends Poly1 {
  implicit def caseInt = at[Int](identity)
  implicit def caseString = at[String](_.length)
  implicit def caseList[T] = at[List[T]](_.length)
}

val sizes = (42 :: "hello" :: List(1, 2, 3) :: HNil).map(size)
// sizes: Int :: Int :: Int :: HNil = 42 :: 5 :: 3 :: HNil

// Heterogeneous maps with type-level relations
class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
implicit val stringToInt = new BiMapIS[String, Int]

val hmap = HMap[BiMapIS](23 -> "twenty-three", "age" -> 30)
val str: Option[String] = hmap.get(23)    // Some("twenty-three")
val int: Option[Int] = hmap.get("age")    // Some(30)

// Function lifting into Option
val add: (Int, Int) => Int = _ + _
val safeAdd = Lift.liftO(add)  // (Option[Int], Option[Int]) => Option[Int]
val result = safeAdd(Some(5), Some(3))  // Some(8)
val failed = safeAdd(Some(5), None)     // None

// Generic transformations with SYB
case class Person(name: String, age: Int)
val person = Person("Alice", 30)

object incrementAge extends Poly1 {
  implicit def caseInt = at[Int](_ + 1)
  implicit def default[T] = at[T](identity)
}

val olderPerson = person.everywhere(incrementAge)
// Person("Alice", 31) - age incremented, name unchanged

Architecture

Shapeless is built around several core concepts that work together to enable generic programming:

  • HList (Heterogeneous Lists): Statically typed lists that can contain elements of different types, providing the foundation for type-safe generic operations
  • Type-level Programming: Natural numbers (Nat) and type-level computations enable compile-time verification and manipulation
  • Polymorphic Functions: Poly functions allow type-specific behavior while maintaining a single interface
  • Isomorphisms: Bidirectional type transformations (Iso) enable safe conversions between equivalent representations
  • Automatic Derivation: Type class instances can be automatically derived for case classes and other algebraic data types
  • Compile-time Safety: All operations are verified at compile time, eliminating runtime type errors

Capabilities

Heterogeneous Lists (HList)

Core data structure for holding statically typed sequences of different types. Provides rich operations for manipulation, transformation, and type-safe access.

sealed trait HList
final case class ::[+H, +T <: HList](head: H, tail: T) extends HList
case object HNil extends HNil

// Enhanced operations through HListOps
class HListOps[L <: HList](l: L) {
  def head(implicit c: IsHCons[L]): c.H
  def tail(implicit c: IsHCons[L]): c.T
  def ::[H](h: H): H :: L
  def ++[S <: HList](suffix: S)(implicit prepend: Prepend[L, S]): prepend.Out
  def reverse(implicit reverse: Reverse[L]): reverse.Out
  def map[HF](f: HF)(implicit mapper: Mapper[HF, L]): mapper.Out
}

HList Operations

Polymorphic Functions

Type-safe polymorphic functions that can have different behavior for different types while maintaining a unified interface.

trait Poly extends Product with Serializable

trait Poly1 extends Poly {
  def at[T] = new Case1Builder[T]
  def apply[T](t: T)(implicit c: Case1[T]): c.R
}

// Natural transformations
trait ~>[F[_], G[_]] extends Poly1 {
  def apply[T](f: F[T]): G[T]
}

Polymorphic Functions

Type-level Natural Numbers

Compile-time natural number arithmetic for expressing constraints and computations at the type level.

trait Nat
case class Succ[P <: Nat]() extends Nat
class _0 extends Nat

// Arithmetic operations
trait Sum[A <: Nat, B <: Nat] { type Out <: Nat }
trait Diff[A <: Nat, B <: Nat] { type Out <: Nat }
trait Prod[A <: Nat, B <: Nat] { type Out <: Nat }

// Comparison operations  
trait LT[A <: Nat, B <: Nat]
type <[A <: Nat, B <: Nat] = LT[A, B]

Type-level Natural Numbers

Type-safe Records

HList-based records with compile-time field access and type-safe manipulation operations.

trait Field[T] extends FieldAux { type valueType = T }
type FieldEntry[F <: FieldAux] = (F, F#valueType)

class RecordOps[L <: HList](l: L) {
  def get[F <: FieldAux](f: F)(implicit selector: Selector[L, FieldEntry[F]]): F#valueType
  def updated[V, F <: Field[V]](f: F, v: V)(implicit updater: Updater[L, F, V]): updater.Out
  def +[V, F <: Field[V]](fv: (F, V))(implicit updater: Updater[L, F, V]): updater.Out
  def -[F <: FieldAux](f: F)(implicit remove: Remove[FieldEntry[F], L]): remove.Out
}

Type-safe Records

Type-safe Conversions

Bidirectional conversions between tuples, functions, and HLists with compile-time safety guarantees.

// Tuple to HList conversion
trait HLister[T <: Product] { type Out <: HList; def apply(t: T): Out }

// Function conversions
trait FnHLister[F] { type Out; def apply(f: F): Out }
trait FnUnHLister[F] { type Out; def apply(f: F): Out }

// Traversable conversions
trait FromTraversable[Out <: HList] { 
  def apply(l: GenTraversable[_]): Option[Out] 
}

Type-safe Conversions

Generic Programming Utilities

Advanced utilities for functional references, navigation, and transformations including lenses, zippers, and isomorphisms.

// Lenses for functional references
trait Lens[C, F] {
  def get(c: C): F
  def set(c: C)(f: F): C
  def modify(c: C)(f: F => F): C
}

// Zippers for navigation and updates
case class Zipper[C, L <: HList, R <: HList, P](prefix: L, suffix: R, parent: P)

// Isomorphisms for bidirectional conversions
trait Iso[T, U] {
  def to(t: T): U
  def from(u: U): T
  def reverse: Iso[U, T]
}

Generic Programming Utilities

Sized Collections

Collections with statically known size, providing compile-time length verification and safe operations.

abstract class Sized[+Repr, L <: Nat](r: Repr) {
  type A
  def unsized = r
}

class SizedOps[A, Repr, L <: Nat] {
  def head(implicit ev: _0 < L): A
  def tail(implicit pred: Pred[L]): Sized[Repr, pred.Out]
  def take[M <: Nat](implicit diff: Diff[L, M]): Sized[Repr, M]
  def drop[M <: Nat](implicit diff: Diff[L, M]): Sized[Repr, diff.Out]
}

Sized Collections

Type Operators and Advanced Types

Type-level logic operations, inequalities, tagged types, and advanced type programming constructs.

type Id[+T] = T
type ¬[T] = T => Nothing
type ∧[T, U] = T with U
type ∨[T, U] = ¬[¬[T] ∧ ¬[U]]

// Type inequalities
trait =:!=[A, B]  // Type inequality witness
trait <:!<[A, B]  // Subtype inequality witness

// Tagged types
type @@[T, U] = T with Tagged[U]

Type Operators

Runtime Type Safety

Runtime type-safe casting with compile-time guarantees through the Typeable type class.

trait Typeable[U] {
  def cast(t: Any): Option[U]
}

class Cast(t: Any) {
  def cast[U](implicit castU: Typeable[U]): Option[U]
}

Runtime Type Safety

Heterogeneous Maps (HMap)

Type-safe maps where keys and values can have different types, with relationships enforced at compile time through type-level relations.

class HMap[R[_, _]](underlying: Map[Any, Any] = Map.empty) extends Poly {
  def get[K, V](k: K)(implicit ev: R[K, V]): Option[V]
  def +[K, V](kv: (K, V))(implicit ev: R[K, V]): HMap[R]
  def -[K](k: K): HMap[R]
}

Heterogeneous Maps

Function Lifting

Utilities for lifting ordinary functions of arbitrary arity into various contexts such as Option, enabling safe handling of potentially absent values.

object Lift {
  def liftO[InF, InL <: HList, R, OInL <: HList, OutF](f: InF): OutF
}

Function Lifting

Scrap Your Boilerplate (SYB)

Generic programming combinators for queries and transformations over arbitrarily nested data structures without explicit recursion.

trait Data[F, T, R] {
  def gmapQ(f: F, t: T): R
}

trait DataT[F, T] {
  def gmapT(f: F, t: T): T
}

def everything[F <: Poly](f: F): ...
def everywhere[F <: Poly](f: F): ...

Scrap Your Boilerplate