CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Pending
Overview
Eval results
Files

hlist.mddocs/

HList Operations

Heterogeneous Lists (HLists) are the foundation of shapeless, providing statically typed sequences that can contain elements of different types. Unlike regular Scala Lists, HLists preserve type information for each element at compile time, enabling type-safe operations without runtime type checking.

Core Types

HList Base Type

/**
 * Base trait for heterogeneous lists - lists that can contain elements of different types
 */
sealed trait HList

HList Construction

/**
 * Non-empty HList with head element of type H and tail of type T
 */
final case class ::[+H, +T <: HList](head: H, tail: T) extends HList

/**
 * Empty HList type and singleton value
 */
trait HNil extends HList
case object HNil extends HNil

Usage Examples:

import shapeless._

// Creating HLists
val empty: HNil = HNil
val numbers: Int :: String :: Boolean :: HNil = 42 :: "hello" :: true :: HNil
val mixed = 1.5 :: 'c' :: List(1, 2, 3) :: HNil

Enhanced Operations

HListOps

All HLists are automatically enhanced with rich operations through HListOps[L <: HList]:

/**
 * Enhanced operations for HLists, providing rich functionality
 */
class HListOps[L <: HList](l: L) {
  // Access operations
  def head(implicit c: IsHCons[L]): c.H
  def tail(implicit c: IsHCons[L]): c.T
  def apply[N <: Nat](implicit at: At[L, N]): at.Out
  def apply[N <: Nat](n: N)(implicit at: At[L, N]): at.Out
  def last(implicit last: Last[L]): last.Out
  def init(implicit init: Init[L]): init.Out
  def select[U](implicit selector: Selector[L, U]): U
}

Access Operations

Head and Tail

/**
 * Get the head element (requires non-empty HList evidence)
 */
def head(implicit c: IsHCons[L]): c.H

/**
 * Get the tail (all elements except the first)
 */
def tail(implicit c: IsHCons[L]): c.T

Usage Examples:

import shapeless._

val hlist = 42 :: "hello" :: true :: HNil
val h: Int = hlist.head           // 42
val t = hlist.tail                // "hello" :: true :: HNil
val h2: String = hlist.tail.head  // "hello"

Indexed Access

/**
 * Get nth element by type-level index
 */
def apply[N <: Nat](implicit at: At[L, N]): at.Out
def apply[N <: Nat](n: N)(implicit at: At[L, N]): at.Out

Usage Examples:

import shapeless._

val hlist = 42 :: "hello" :: true :: HNil
val first: Int = hlist(0)      // 42
val second: String = hlist(1)  // "hello"  
val third: Boolean = hlist(2)  // true

Type-based Selection

/**
 * Get first element of type U
 */
def select[U](implicit selector: Selector[L, U]): U

Usage Examples:

import shapeless._

val hlist = 42 :: "hello" :: true :: 3.14 :: HNil
val str: String = hlist.select[String]     // "hello"
val bool: Boolean = hlist.select[Boolean]  // true
val double: Double = hlist.select[Double]  // 3.14

Manipulation Operations

Prepending and Appending

/**
 * Prepend element to front of HList
 */
def ::[H](h: H): H :: L
def +:[H](h: H): H :: L  // Alias for ::

/**
 * Append element to end of HList
 */
def :+[T](t: T)(implicit prepend: Prepend[L, T :: HNil]): prepend.Out

/**
 * Append another HList
 */
def ++[S <: HList](suffix: S)(implicit prepend: Prepend[L, S]): prepend.Out
def ++:[P <: HList](prefix: P)(implicit prepend: Prepend[P, L]): prepend.Out
def :::[P <: HList](prefix: P)(implicit prepend: Prepend[P, L]): prepend.Out

Usage Examples:

import shapeless._

val hlist = "hello" :: true :: HNil
val prepended = 42 :: hlist        // Int :: String :: Boolean :: HNil  
val appended = hlist :+ 3.14       // String :: Boolean :: Double :: HNil
val combined = hlist ++ (1 :: 2 :: HNil)  // String :: Boolean :: Int :: Int :: HNil

Filtering Operations

/**
 * Get all elements of type U
 */
def filter[U](implicit filter: Filter[L, U]): filter.Out

/**
 * Get elements that are NOT of type U
 */
def filterNot[U](implicit filter: FilterNot[L, U]): filter.Out

/**
 * Remove first element of type U, returning both the element and remaining HList
 */
def removeElem[U](implicit remove: Remove[U, L]): (U, remove.Out)

/**
 * Remove multiple specified elements
 */
def removeAll[SL <: HList](implicit removeAll: RemoveAll[SL, L]): (SL, removeAll.Out)

Usage Examples:

import shapeless._

val mixed = 1 :: "a" :: 2 :: "b" :: 3 :: "c" :: HNil
val strings = mixed.filter[String]        // "a" :: "b" :: "c" :: HNil
val notStrings = mixed.filterNot[String]  // 1 :: 2 :: 3 :: HNil

val (removed, remaining) = mixed.removeElem[String]  // ("a", 1 :: 2 :: "b" :: 3 :: "c" :: HNil)

Update Operations

/**
 * Replace first element of type U with new value
 */
def replace[U](u: U)(implicit replacer: Replacer[L, U, U]): (U, replacer.Out)

/**
 * Update first element of type U with new value
 */
def updatedElem[U](u: U)(implicit replacer: Replacer[L, U, U]): replacer.Out

/**
 * Update element at type-level position N
 */
def updatedAt[N <: Nat, U](n: N, u: U)(implicit updater: ReplaceAt[L, N, U]): updater.Out

Usage Examples:

import shapeless._

val hlist = 1 :: "hello" :: true :: HNil
val updated = hlist.updatedElem("world")    // 1 :: "world" :: true :: HNil
val updatedAt = hlist.updatedAt(0, 99)      // 99 :: "hello" :: true :: HNil

Slicing Operations

Take and Drop

/**
 * Take first N elements
 */
def take[N <: Nat](implicit take: Take[L, N]): take.Out

/**
 * Drop first N elements  
 */
def drop[N <: Nat](implicit drop: Drop[L, N]): drop.Out

/**
 * Split at position N, returning (prefix, suffix)
 */
def split[N <: Nat](implicit split: Split[L, N]): split.Out

Usage Examples:

import shapeless._

val hlist = 1 :: "hello" :: true :: 3.14 :: HNil
val first2 = hlist.take(2)     // 1 :: "hello" :: HNil
val rest = hlist.drop(2)       // true :: 3.14 :: HNil  
val (prefix, suffix) = hlist.split(2)  // (1 :: "hello" :: HNil, true :: 3.14 :: HNil)

Type-based Splitting

/**
 * Split at first occurrence of type U
 */
def splitLeft[U](implicit splitLeft: SplitLeft[L, U]): splitLeft.Out

/**
 * Split at last occurrence of type U
 */
def splitRight[U](implicit splitRight: SplitRight[L, U]): splitRight.Out

Transform Operations

Structural Transforms

/**
 * Reverse the HList
 */
def reverse(implicit reverse: Reverse[L]): reverse.Out

/**
 * Get length as type-level natural number
 */
def length(implicit length: Length[L]): length.Out

Usage Examples:

import shapeless._

val hlist = 1 :: "hello" :: true :: HNil
val reversed = hlist.reverse  // true :: "hello" :: 1 :: HNil
val len = hlist.length       // Succ[Succ[Succ[_0]]] (type-level 3)

Polymorphic Mapping

/**
 * Map polymorphic function over HList elements
 */
def map[HF](f: HF)(implicit mapper: Mapper[HF, L]): mapper.Out

/**
 * FlatMap polymorphic function over HList elements
 */
def flatMap[HF](f: HF)(implicit mapper: FlatMapper[HF, L]): mapper.Out

/**
 * Replace all elements with constant value
 */
def mapConst[C](c: C)(implicit mapper: ConstMapper[C, L]): mapper.Out

Usage Examples:

import shapeless._

object showSize extends Poly1 {
  implicit def caseInt = at[Int](_.toString.length)
  implicit def caseString = at[String](_.length)
  implicit def caseBoolean = at[Boolean](_.toString.length)
}

val hlist = 42 :: "hello" :: true :: HNil
val sizes = hlist.map(showSize)  // 2 :: 5 :: 4 :: HNil

val allOnes = hlist.mapConst(1)  // 1 :: 1 :: 1 :: HNil

Fold Operations

Folding and Reducing

/**
 * Left fold with polymorphic function
 */
def foldLeft[R, HF](z: R)(op: HF)(implicit folder: LeftFolder[L, R, HF]): folder.Out

/**
 * Right fold with polymorphic function  
 */
def foldRight[R, HF](z: R)(op: HF)(implicit folder: RightFolder[L, R, HF]): folder.Out

/**
 * Left reduce (fold without initial value)
 */
def reduceLeft[HF](op: HF)(implicit reducer: LeftReducer[L, HF]): reducer.Out

/**
 * Right reduce  
 */
def reduceRight[HF](op: HF)(implicit reducer: RightReducer[L, HF]): reducer.Out

Usage Examples:

import shapeless._

object combine extends Poly2 {
  implicit def stringInt = at[String, Int]((s, i) => s + i.toString)
  implicit def stringString = at[String, String](_ + _)
  implicit def stringBoolean = at[String, Boolean]((s, b) => s + b.toString)
}

val hlist = 42 :: "hello" :: true :: HNil  
val result = hlist.foldLeft("")(combine)  // "42hellotrue"

Zip Operations

Zipping HLists

/**
 * Zip with another HList
 */
def zip[R <: HList](r: R)(implicit zipper: Zip[L :: R :: HNil]): zipper.Out

/**
 * Transpose HList (zip HList of HLists)
 */
def transpose(implicit transpose: Transposer[L]): transpose.Out

/**
 * Unzip HList of tuples
 */
def unzipped(implicit unzipper: Unzip[L]): unzipper.Out

Usage Examples:

import shapeless._

val left = 1 :: "hello" :: true :: HNil  
val right = 2.0 :: "world" :: false :: HNil
val zipped = left.zip(right)  // (1, 2.0) :: ("hello", "world") :: (true, false) :: HNil

val tuples = (1, 'a') :: (2, 'b') :: (3, 'c') :: HNil
val (ints, chars) = tuples.unzipped  // (1 :: 2 :: 3 :: HNil, 'a' :: 'b' :: 'c' :: HNil)

Conversion Operations

Type Conversions

/**
 * Unify to common supertype
 */
def unify(implicit unifier: Unifier[L]): unifier.Out

/**
 * Convert to tuple (if possible)
 */
def tupled(implicit tupler: Tupler[L]): tupler.Out

/**
 * Convert to List of common supertype
 */
def toList[Lub](implicit toList: ToList[L, Lub]): List[Lub]

/**
 * Convert to Array of common supertype
 */
def toArray[Lub](implicit toArray: ToArray[L, Lub]): Array[Lub]

Usage Examples:

import shapeless._

val numbers = 1 :: 2 :: 3 :: HNil
val tuple = numbers.tupled        // (1, 2, 3)
val list = numbers.toList        // List(1, 2, 3)

val mixed = 1 :: 2.0 :: 3 :: HNil  
val unified = mixed.toList[AnyVal]  // List(1, 2.0, 3)

Type Classes

Core Type Classes

The HList operations are powered by type classes that provide compile-time evidence and computation:

/**
 * Witnesses that an HList is non-empty (composite)
 */
trait IsHCons[L <: HList] {
  type H          // Head type
  type T <: HList // Tail type
  def head(l: L): H
  def tail(l: L): T
}

/**
 * Computes the length of an HList at the type level
 */
trait Length[L <: HList] {
  type Out <: Nat  // Length as natural number
  def apply(): Out
}

/**
 * Type-level append operation
 */
trait Prepend[P <: HList, S <: HList] {
  type Out <: HList
  def apply(prefix: P, suffix: S): Out
}

/**
 * Polymorphic function mapping over HList
 */
trait Mapper[HF, In <: HList] {
  type Out <: HList
  def apply(f: HF, l: In): Out  
}

/**
 * Type-based element selection
 */
trait Selector[L <: HList, U] {
  def apply(l: L): U
}

/**
 * Type-based element filtering
 */
trait Filter[L <: HList, U] {
  type Out <: HList
  def apply(l: L): Out
}

These type classes enable shapeless to verify operations at compile time and provide rich type-level programming capabilities while maintaining runtime safety.

Install with Tessl CLI

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

docs

conversions.md

generic.md

hlist.md

hmap.md

index.md

lift.md

nat.md

poly.md

records.md

sized.md

sybclass.md

typeable.md

typeoperators.md

tile.json