0
# HList Operations
1
2
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.
3
4
## Core Types
5
6
### HList Base Type
7
8
```scala { .api }
9
/**
10
* Base trait for heterogeneous lists - lists that can contain elements of different types
11
*/
12
sealed trait HList
13
```
14
15
### HList Construction
16
17
```scala { .api }
18
/**
19
* Non-empty HList with head element of type H and tail of type T
20
*/
21
final case class ::[+H, +T <: HList](head: H, tail: T) extends HList
22
23
/**
24
* Empty HList type and singleton value
25
*/
26
trait HNil extends HList
27
case object HNil extends HNil
28
```
29
30
**Usage Examples:**
31
32
```scala
33
import shapeless._
34
35
// Creating HLists
36
val empty: HNil = HNil
37
val numbers: Int :: String :: Boolean :: HNil = 42 :: "hello" :: true :: HNil
38
val mixed = 1.5 :: 'c' :: List(1, 2, 3) :: HNil
39
```
40
41
## Enhanced Operations
42
43
### HListOps
44
45
All HLists are automatically enhanced with rich operations through `HListOps[L <: HList]`:
46
47
```scala { .api }
48
/**
49
* Enhanced operations for HLists, providing rich functionality
50
*/
51
class HListOps[L <: HList](l: L) {
52
// Access operations
53
def head(implicit c: IsHCons[L]): c.H
54
def tail(implicit c: IsHCons[L]): c.T
55
def apply[N <: Nat](implicit at: At[L, N]): at.Out
56
def apply[N <: Nat](n: N)(implicit at: At[L, N]): at.Out
57
def last(implicit last: Last[L]): last.Out
58
def init(implicit init: Init[L]): init.Out
59
def select[U](implicit selector: Selector[L, U]): U
60
}
61
```
62
63
## Access Operations
64
65
### Head and Tail
66
67
```scala { .api }
68
/**
69
* Get the head element (requires non-empty HList evidence)
70
*/
71
def head(implicit c: IsHCons[L]): c.H
72
73
/**
74
* Get the tail (all elements except the first)
75
*/
76
def tail(implicit c: IsHCons[L]): c.T
77
```
78
79
**Usage Examples:**
80
81
```scala
82
import shapeless._
83
84
val hlist = 42 :: "hello" :: true :: HNil
85
val h: Int = hlist.head // 42
86
val t = hlist.tail // "hello" :: true :: HNil
87
val h2: String = hlist.tail.head // "hello"
88
```
89
90
### Indexed Access
91
92
```scala { .api }
93
/**
94
* Get nth element by type-level index
95
*/
96
def apply[N <: Nat](implicit at: At[L, N]): at.Out
97
def apply[N <: Nat](n: N)(implicit at: At[L, N]): at.Out
98
```
99
100
**Usage Examples:**
101
102
```scala
103
import shapeless._
104
105
val hlist = 42 :: "hello" :: true :: HNil
106
val first: Int = hlist(0) // 42
107
val second: String = hlist(1) // "hello"
108
val third: Boolean = hlist(2) // true
109
```
110
111
### Type-based Selection
112
113
```scala { .api }
114
/**
115
* Get first element of type U
116
*/
117
def select[U](implicit selector: Selector[L, U]): U
118
```
119
120
**Usage Examples:**
121
122
```scala
123
import shapeless._
124
125
val hlist = 42 :: "hello" :: true :: 3.14 :: HNil
126
val str: String = hlist.select[String] // "hello"
127
val bool: Boolean = hlist.select[Boolean] // true
128
val double: Double = hlist.select[Double] // 3.14
129
```
130
131
## Manipulation Operations
132
133
### Prepending and Appending
134
135
```scala { .api }
136
/**
137
* Prepend element to front of HList
138
*/
139
def ::[H](h: H): H :: L
140
def +:[H](h: H): H :: L // Alias for ::
141
142
/**
143
* Append element to end of HList
144
*/
145
def :+[T](t: T)(implicit prepend: Prepend[L, T :: HNil]): prepend.Out
146
147
/**
148
* Append another HList
149
*/
150
def ++[S <: HList](suffix: S)(implicit prepend: Prepend[L, S]): prepend.Out
151
def ++:[P <: HList](prefix: P)(implicit prepend: Prepend[P, L]): prepend.Out
152
def :::[P <: HList](prefix: P)(implicit prepend: Prepend[P, L]): prepend.Out
153
```
154
155
**Usage Examples:**
156
157
```scala
158
import shapeless._
159
160
val hlist = "hello" :: true :: HNil
161
val prepended = 42 :: hlist // Int :: String :: Boolean :: HNil
162
val appended = hlist :+ 3.14 // String :: Boolean :: Double :: HNil
163
val combined = hlist ++ (1 :: 2 :: HNil) // String :: Boolean :: Int :: Int :: HNil
164
```
165
166
### Filtering Operations
167
168
```scala { .api }
169
/**
170
* Get all elements of type U
171
*/
172
def filter[U](implicit filter: Filter[L, U]): filter.Out
173
174
/**
175
* Get elements that are NOT of type U
176
*/
177
def filterNot[U](implicit filter: FilterNot[L, U]): filter.Out
178
179
/**
180
* Remove first element of type U, returning both the element and remaining HList
181
*/
182
def removeElem[U](implicit remove: Remove[U, L]): (U, remove.Out)
183
184
/**
185
* Remove multiple specified elements
186
*/
187
def removeAll[SL <: HList](implicit removeAll: RemoveAll[SL, L]): (SL, removeAll.Out)
188
```
189
190
**Usage Examples:**
191
192
```scala
193
import shapeless._
194
195
val mixed = 1 :: "a" :: 2 :: "b" :: 3 :: "c" :: HNil
196
val strings = mixed.filter[String] // "a" :: "b" :: "c" :: HNil
197
val notStrings = mixed.filterNot[String] // 1 :: 2 :: 3 :: HNil
198
199
val (removed, remaining) = mixed.removeElem[String] // ("a", 1 :: 2 :: "b" :: 3 :: "c" :: HNil)
200
```
201
202
### Update Operations
203
204
```scala { .api }
205
/**
206
* Replace first element of type U with new value
207
*/
208
def replace[U](u: U)(implicit replacer: Replacer[L, U, U]): (U, replacer.Out)
209
210
/**
211
* Update first element of type U with new value
212
*/
213
def updatedElem[U](u: U)(implicit replacer: Replacer[L, U, U]): replacer.Out
214
215
/**
216
* Update element at type-level position N
217
*/
218
def updatedAt[N <: Nat, U](n: N, u: U)(implicit updater: ReplaceAt[L, N, U]): updater.Out
219
```
220
221
**Usage Examples:**
222
223
```scala
224
import shapeless._
225
226
val hlist = 1 :: "hello" :: true :: HNil
227
val updated = hlist.updatedElem("world") // 1 :: "world" :: true :: HNil
228
val updatedAt = hlist.updatedAt(0, 99) // 99 :: "hello" :: true :: HNil
229
```
230
231
## Slicing Operations
232
233
### Take and Drop
234
235
```scala { .api }
236
/**
237
* Take first N elements
238
*/
239
def take[N <: Nat](implicit take: Take[L, N]): take.Out
240
241
/**
242
* Drop first N elements
243
*/
244
def drop[N <: Nat](implicit drop: Drop[L, N]): drop.Out
245
246
/**
247
* Split at position N, returning (prefix, suffix)
248
*/
249
def split[N <: Nat](implicit split: Split[L, N]): split.Out
250
```
251
252
**Usage Examples:**
253
254
```scala
255
import shapeless._
256
257
val hlist = 1 :: "hello" :: true :: 3.14 :: HNil
258
val first2 = hlist.take(2) // 1 :: "hello" :: HNil
259
val rest = hlist.drop(2) // true :: 3.14 :: HNil
260
val (prefix, suffix) = hlist.split(2) // (1 :: "hello" :: HNil, true :: 3.14 :: HNil)
261
```
262
263
### Type-based Splitting
264
265
```scala { .api }
266
/**
267
* Split at first occurrence of type U
268
*/
269
def splitLeft[U](implicit splitLeft: SplitLeft[L, U]): splitLeft.Out
270
271
/**
272
* Split at last occurrence of type U
273
*/
274
def splitRight[U](implicit splitRight: SplitRight[L, U]): splitRight.Out
275
```
276
277
## Transform Operations
278
279
### Structural Transforms
280
281
```scala { .api }
282
/**
283
* Reverse the HList
284
*/
285
def reverse(implicit reverse: Reverse[L]): reverse.Out
286
287
/**
288
* Get length as type-level natural number
289
*/
290
def length(implicit length: Length[L]): length.Out
291
```
292
293
**Usage Examples:**
294
295
```scala
296
import shapeless._
297
298
val hlist = 1 :: "hello" :: true :: HNil
299
val reversed = hlist.reverse // true :: "hello" :: 1 :: HNil
300
val len = hlist.length // Succ[Succ[Succ[_0]]] (type-level 3)
301
```
302
303
### Polymorphic Mapping
304
305
```scala { .api }
306
/**
307
* Map polymorphic function over HList elements
308
*/
309
def map[HF](f: HF)(implicit mapper: Mapper[HF, L]): mapper.Out
310
311
/**
312
* FlatMap polymorphic function over HList elements
313
*/
314
def flatMap[HF](f: HF)(implicit mapper: FlatMapper[HF, L]): mapper.Out
315
316
/**
317
* Replace all elements with constant value
318
*/
319
def mapConst[C](c: C)(implicit mapper: ConstMapper[C, L]): mapper.Out
320
```
321
322
**Usage Examples:**
323
324
```scala
325
import shapeless._
326
327
object showSize extends Poly1 {
328
implicit def caseInt = at[Int](_.toString.length)
329
implicit def caseString = at[String](_.length)
330
implicit def caseBoolean = at[Boolean](_.toString.length)
331
}
332
333
val hlist = 42 :: "hello" :: true :: HNil
334
val sizes = hlist.map(showSize) // 2 :: 5 :: 4 :: HNil
335
336
val allOnes = hlist.mapConst(1) // 1 :: 1 :: 1 :: HNil
337
```
338
339
## Fold Operations
340
341
### Folding and Reducing
342
343
```scala { .api }
344
/**
345
* Left fold with polymorphic function
346
*/
347
def foldLeft[R, HF](z: R)(op: HF)(implicit folder: LeftFolder[L, R, HF]): folder.Out
348
349
/**
350
* Right fold with polymorphic function
351
*/
352
def foldRight[R, HF](z: R)(op: HF)(implicit folder: RightFolder[L, R, HF]): folder.Out
353
354
/**
355
* Left reduce (fold without initial value)
356
*/
357
def reduceLeft[HF](op: HF)(implicit reducer: LeftReducer[L, HF]): reducer.Out
358
359
/**
360
* Right reduce
361
*/
362
def reduceRight[HF](op: HF)(implicit reducer: RightReducer[L, HF]): reducer.Out
363
```
364
365
**Usage Examples:**
366
367
```scala
368
import shapeless._
369
370
object combine extends Poly2 {
371
implicit def stringInt = at[String, Int]((s, i) => s + i.toString)
372
implicit def stringString = at[String, String](_ + _)
373
implicit def stringBoolean = at[String, Boolean]((s, b) => s + b.toString)
374
}
375
376
val hlist = 42 :: "hello" :: true :: HNil
377
val result = hlist.foldLeft("")(combine) // "42hellotrue"
378
```
379
380
## Zip Operations
381
382
### Zipping HLists
383
384
```scala { .api }
385
/**
386
* Zip with another HList
387
*/
388
def zip[R <: HList](r: R)(implicit zipper: Zip[L :: R :: HNil]): zipper.Out
389
390
/**
391
* Transpose HList (zip HList of HLists)
392
*/
393
def transpose(implicit transpose: Transposer[L]): transpose.Out
394
395
/**
396
* Unzip HList of tuples
397
*/
398
def unzipped(implicit unzipper: Unzip[L]): unzipper.Out
399
```
400
401
**Usage Examples:**
402
403
```scala
404
import shapeless._
405
406
val left = 1 :: "hello" :: true :: HNil
407
val right = 2.0 :: "world" :: false :: HNil
408
val zipped = left.zip(right) // (1, 2.0) :: ("hello", "world") :: (true, false) :: HNil
409
410
val tuples = (1, 'a') :: (2, 'b') :: (3, 'c') :: HNil
411
val (ints, chars) = tuples.unzipped // (1 :: 2 :: 3 :: HNil, 'a' :: 'b' :: 'c' :: HNil)
412
```
413
414
## Conversion Operations
415
416
### Type Conversions
417
418
```scala { .api }
419
/**
420
* Unify to common supertype
421
*/
422
def unify(implicit unifier: Unifier[L]): unifier.Out
423
424
/**
425
* Convert to tuple (if possible)
426
*/
427
def tupled(implicit tupler: Tupler[L]): tupler.Out
428
429
/**
430
* Convert to List of common supertype
431
*/
432
def toList[Lub](implicit toList: ToList[L, Lub]): List[Lub]
433
434
/**
435
* Convert to Array of common supertype
436
*/
437
def toArray[Lub](implicit toArray: ToArray[L, Lub]): Array[Lub]
438
```
439
440
**Usage Examples:**
441
442
```scala
443
import shapeless._
444
445
val numbers = 1 :: 2 :: 3 :: HNil
446
val tuple = numbers.tupled // (1, 2, 3)
447
val list = numbers.toList // List(1, 2, 3)
448
449
val mixed = 1 :: 2.0 :: 3 :: HNil
450
val unified = mixed.toList[AnyVal] // List(1, 2.0, 3)
451
```
452
453
## Type Classes
454
455
### Core Type Classes
456
457
The HList operations are powered by type classes that provide compile-time evidence and computation:
458
459
```scala { .api }
460
/**
461
* Witnesses that an HList is non-empty (composite)
462
*/
463
trait IsHCons[L <: HList] {
464
type H // Head type
465
type T <: HList // Tail type
466
def head(l: L): H
467
def tail(l: L): T
468
}
469
470
/**
471
* Computes the length of an HList at the type level
472
*/
473
trait Length[L <: HList] {
474
type Out <: Nat // Length as natural number
475
def apply(): Out
476
}
477
478
/**
479
* Type-level append operation
480
*/
481
trait Prepend[P <: HList, S <: HList] {
482
type Out <: HList
483
def apply(prefix: P, suffix: S): Out
484
}
485
486
/**
487
* Polymorphic function mapping over HList
488
*/
489
trait Mapper[HF, In <: HList] {
490
type Out <: HList
491
def apply(f: HF, l: In): Out
492
}
493
494
/**
495
* Type-based element selection
496
*/
497
trait Selector[L <: HList, U] {
498
def apply(l: L): U
499
}
500
501
/**
502
* Type-based element filtering
503
*/
504
trait Filter[L <: HList, U] {
505
type Out <: HList
506
def apply(l: L): Out
507
}
508
```
509
510
These type classes enable shapeless to verify operations at compile time and provide rich type-level programming capabilities while maintaining runtime safety.