or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

hlist.mddocs/

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.