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

generic.mddocs/

0

# Generic Programming Utilities

1

2

Shapeless provides advanced utilities for generic programming including lenses for functional references, zippers for navigation and updates, isomorphisms for bidirectional conversions, and various other tools that enable powerful abstractions over data structures.

3

4

## Lenses

5

6

Lenses provide functional references to parts of immutable data structures, enabling safe updates without mutation.

7

8

### Core Lens Type

9

10

```scala { .api }

11

/**

12

* Functional reference to a field F within container C

13

*/

14

trait Lens[C, F] {

15

def get(c: C): F

16

def set(c: C)(f: F): C

17

def modify(c: C)(f: F => F): C = set(c)(f(get(c)))

18

}

19

```

20

21

### Lens Operations

22

23

```scala { .api }

24

/**

25

* Compose with another lens

26

*/

27

def compose[D](g: Lens[D, C]): Lens[D, F]

28

29

/**

30

* Index into HList field (when F is an HList)

31

*/

32

def >>[L <: HList, N <: Nat](n: N)

33

(implicit iso: Iso[F, L], lens: HListNthLens[L, N]): Lens[C, lens.Elem]

34

```

35

36

### Lens Factory Methods

37

38

```scala { .api }

39

object Lens {

40

/**

41

* Identity lens

42

*/

43

def apply[C] = id[C]

44

def id[C]: Lens[C, C]

45

46

/**

47

* Set membership lens

48

*/

49

def setLens[E](e: E): Lens[Set[E], Boolean]

50

51

/**

52

* Map value lens

53

*/

54

def mapLens[K, V](k: K): Lens[Map[K, V], Option[V]]

55

56

/**

57

* HList position lens

58

*/

59

def hlistNthLens[L <: HList, N <: Nat]: HListNthLens[L, N]

60

}

61

```

62

63

**Usage Examples:**

64

65

```scala

66

import shapeless._

67

import shapeless.lens._

68

69

case class Address(street: String, city: String, zip: String)

70

case class Person(name: String, age: Int, address: Address)

71

72

val person = Person("Alice", 30, Address("123 Main St", "Boston", "02101"))

73

74

// Create lenses (typically done with macro support in real usage)

75

val nameLens = Lens[Person, String](_.name, p => n => p.copy(name = n))

76

val ageLens = Lens[Person, Int](_.age, p => a => p.copy(age = a))

77

val addressLens = Lens[Person, Address](_.address, p => a => p.copy(address = a))

78

79

// Use lenses

80

val name = nameLens.get(person) // "Alice"

81

val olderPerson = ageLens.set(person)(31) // Person with age = 31

82

val happyPerson = nameLens.modify(person)(_ + " Smith") // "Alice Smith"

83

84

// Compose lenses for nested access

85

val streetLens = Lens[Address, String](_.street, a => s => a.copy(street = s))

86

val personStreetLens = addressLens.compose(streetLens)

87

88

val street = personStreetLens.get(person) // "123 Main St"

89

val movedPerson = personStreetLens.set(person)("456 Oak Ave")

90

```

91

92

### HList Lenses

93

94

```scala { .api }

95

/**

96

* Lens for accessing nth element of HList

97

*/

98

trait HListNthLens[L <: HList, N <: Nat] {

99

type Elem

100

def get(l: L): Elem

101

def set(l: L)(e: Elem): L

102

def toLens: Lens[L, Elem]

103

}

104

```

105

106

**Usage Examples:**

107

108

```scala

109

import shapeless._

110

111

val hlist = "hello" :: 42 :: true :: 3.14 :: HNil

112

113

// Access by index

114

val lens0 = Lens.hlistNthLens[String :: Int :: Boolean :: Double :: HNil, _0]

115

val lens1 = Lens.hlistNthLens[String :: Int :: Boolean :: Double :: HNil, _1]

116

117

val first: String = lens0.get(hlist) // "hello"

118

val second: Int = lens1.get(hlist) // 42

119

120

val updated = lens1.set(hlist)(99) // "hello" :: 99 :: true :: 3.14 :: HNil

121

```

122

123

### Product Lenses

124

125

```scala { .api }

126

/**

127

* Combine multiple lenses into a product lens

128

*/

129

trait ProductLens[C, P <: Product] extends Lens[C, P] {

130

def ~[T, L <: HList, LT <: HList, Q <: Product](other: Lens[C, T]): ProductLens[C, Q]

131

}

132

```

133

134

**Usage Examples:**

135

136

```scala

137

import shapeless._

138

139

case class User(id: Int, name: String, email: String, active: Boolean)

140

val user = User(1, "Bob", "bob@example.com", true)

141

142

// Create individual lenses

143

val idLens = Lens[User, Int](_.id, u => i => u.copy(id = i))

144

val nameLens = Lens[User, String](_.name, u => n => u.copy(name = n))

145

146

// Combine into product lens

147

val idNameLens = idLens ~ nameLens // ProductLens[User, (Int, String)]

148

149

val (id, name) = idNameLens.get(user) // (1, "Bob")

150

val updated = idNameLens.set(user)(2, "Robert") // User(2, "Robert", ...)

151

```

152

153

## Zippers

154

155

Zippers provide cursors for navigating and updating tree-like data structures.

156

157

### Core Zipper Type

158

159

```scala { .api }

160

/**

161

* Generic zipper for navigating and updating data structures

162

* C - Container type, L - Left context, R - Right context, P - Parent context

163

*/

164

case class Zipper[C, L <: HList, R <: HList, P](prefix: L, suffix: R, parent: P)

165

```

166

167

### Navigation Operations

168

169

```scala { .api }

170

// Horizontal movement

171

def right(implicit right: Right[Self]): right.Out

172

def left(implicit left: Left[Self]): left.Out

173

def first(implicit first: First[Self]): first.Out

174

def last(implicit last: Last[Self]): last.Out

175

176

// Positional movement

177

def rightBy[N <: Nat](implicit rightBy: RightBy[Self, N]): rightBy.Out

178

def leftBy[N <: Nat](implicit leftBy: LeftBy[Self, N]): leftBy.Out

179

def rightTo[T](implicit rightTo: RightTo[Self, T]): rightTo.Out

180

def leftTo[T](implicit leftTo: LeftTo[Self, T]): leftTo.Out

181

182

// Vertical movement

183

def up(implicit up: Up[Self]): up.Out

184

def down(implicit down: Down[Self]): down.Out

185

def root(implicit root: Root[Self]): root.Out

186

```

187

188

### Modification Operations

189

190

```scala { .api }

191

// Access and update

192

def get(implicit get: Get[Self]): get.Out

193

def put[E](e: E)(implicit put: Put[Self, E]): put.Out

194

def insert[E](e: E)(implicit insert: Insert[Self, E]): insert.Out

195

def delete(implicit delete: Delete[Self]): delete.Out

196

197

// Reification

198

def reify(implicit reify: Reify[Self]): reify.Out

199

```

200

201

**Usage Examples:**

202

203

```scala

204

import shapeless._

205

206

val hlist = 1 :: "hello" :: true :: 3.14 :: HNil

207

val zipper = hlist.toZipper

208

209

// Navigate and access

210

val atFirst = zipper.first

211

val current = atFirst.get // 1

212

val atSecond = atFirst.right

213

val secondValue = atSecond.get // "hello"

214

215

// Modify

216

val withNewValue = atSecond.put("world")

217

val result = withNewValue.reify // 1 :: "world" :: true :: 3.14 :: HNil

218

219

// Insert and delete

220

val withInserted = atSecond.insert("inserted")

221

val afterDeletion = withInserted.right.delete

222

val final = afterDeletion.reify

223

```

224

225

## Isomorphisms

226

227

Isomorphisms represent bidirectional conversions between types.

228

229

### Core Isomorphism Type

230

231

```scala { .api }

232

/**

233

* Bidirectional transformation between types T and U

234

*/

235

trait Iso[T, U] {

236

def to(t: T): U

237

def from(u: U): T

238

def reverse: Iso[U, T]

239

}

240

```

241

242

### Isomorphism Factory

243

244

```scala { .api }

245

object Iso extends LowPriorityIso {

246

/**

247

* Special case for single-element case classes

248

*/

249

def hlist[CC, T](apply: T => CC, unapply: CC => Option[T]): Iso[CC, T :: HNil]

250

251

/**

252

* General case class iso factory

253

*/

254

def hlist[CC, C, T <: Product, L <: HList](apply: C, unapply: CC => Option[T]): Iso[CC, L]

255

256

// Implicit isos

257

implicit def tupleHListIso[T <: Product, L <: HList]: Iso[T, L]

258

implicit def fnHListFnIso[F, L <: HList, R]: Iso[F, L => R]

259

implicit def identityIso[T]: Iso[T, T]

260

}

261

```

262

263

**Usage Examples:**

264

265

```scala

266

import shapeless._

267

268

case class Point(x: Double, y: Double)

269

270

// Create isomorphisms (typically auto-derived)

271

val pointIso: Iso[Point, Double :: Double :: HNil] =

272

Iso.hlist(Point.apply _, Point.unapply _)

273

274

val point = Point(3.0, 4.0)

275

val hlist = pointIso.to(point) // 3.0 :: 4.0 :: HNil

276

val backToPoint = pointIso.from(hlist) // Point(3.0, 4.0)

277

278

// Use with generic operations

279

val doubled = hlist.map(new (Double => Double)(_ * 2))

280

val doubledPoint = pointIso.from(doubled) // Point(6.0, 8.0)

281

```

282

283

## Type Operators

284

285

Advanced type-level programming constructs.

286

287

### Basic Type Functions

288

289

```scala { .api }

290

type Id[+T] = T

291

type Const[C] = { type λ[T] = C }

292

```

293

294

### Logic Operations

295

296

```scala { .api }

297

type ¬[T] = T => Nothing // Negation

298

type ¬¬[T] = ¬[¬[T]] // Double negation

299

type ∧[T, U] = T with U // Conjunction

300

type ∨[T, U] = ¬[¬[T] ∧ ¬[U]] // Disjunction

301

```

302

303

### Type Inequalities

304

305

```scala { .api }

306

/**

307

* Type inequality witness

308

*/

309

trait =:!=[A, B]

310

implicit def neq[A, B]: A =:!= B

311

implicit def neqAmbig1[A]: A =:!= A = ??? // Ambiguous for equality

312

implicit def neqAmbig2[A]: A =:!= A = ???

313

314

/**

315

* Subtype inequality witness

316

*/

317

trait <:!<[A, B]

318

implicit def nsub[A, B]: A <:!< B

319

implicit def nsubAmbig1[A, B >: A]: A <:!< B = ??? // Ambiguous for subtyping

320

implicit def nsubAmbig2[A, B >: A]: A <:!< B = ???

321

```

322

323

**Usage Examples:**

324

325

```scala

326

import shapeless._

327

328

// Ensure types are different

329

def processIfDifferent[A, B](a: A, b: B)(implicit ev: A =:!= B): String =

330

s"Processing different types: $a and $b"

331

332

val result1 = processIfDifferent(42, "hello") // Works

333

// val result2 = processIfDifferent(42, 24) // Error: Int =:!= Int fails

334

335

// Ensure no subtype relationship

336

def requireUnrelated[A, B](a: A, b: B)(implicit ev1: A <:!< B, ev2: B <:!< A): Unit = ()

337

338

requireUnrelated("hello", 42) // Works: String and Int unrelated

339

// requireUnrelated("hello", "world") // Error: String <:!< String fails

340

```

341

342

### Tagged Types

343

344

```scala { .api }

345

/**

346

* Tagged type T with tag U

347

*/

348

trait Tagged[U]

349

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

350

351

class Tagger[U] {

352

def apply[T](t: T): T @@ U = t.asInstanceOf[T @@ U]

353

}

354

355

def tag[U] = new Tagger[U]

356

```

357

358

**Usage Examples:**

359

360

```scala

361

import shapeless._

362

import tag._

363

364

// Create tagged types for type safety

365

trait UserId

366

trait ProductId

367

368

val userId: Int @@ UserId = tag[UserId](12345)

369

val productId: Int @@ ProductId = tag[ProductId](67890)

370

371

def lookupUser(id: Int @@ UserId): String = s"User ${id}"

372

def lookupProduct(id: Int @@ ProductId): String = s"Product ${id}"

373

374

val user = lookupUser(userId) // Works

375

val product = lookupProduct(productId) // Works

376

// val wrong = lookupUser(productId) // Error: type mismatch

377

```

378

379

### Newtypes

380

381

```scala { .api }

382

/**

383

* Newtype wrapper with operations

384

*/

385

type Newtype[Repr, Ops] = Any @@ NewtypeTag[Repr, Ops]

386

trait NewtypeTag[Repr, Ops]

387

388

def newtype[Repr, Ops](r: Repr): Newtype[Repr, Ops]

389

implicit def newtypeOps[Repr, Ops](t: Newtype[Repr, Ops])

390

(implicit mkOps: Repr => Ops): Ops

391

```

392

393

## Advanced Generic Operations

394

395

### Type Class Derivation

396

397

```scala { .api }

398

/**

399

* Abstracts over type class derivation for product types

400

*/

401

trait TypeClass[C[_]] {

402

def product[H, T <: HList](CHead: C[H], CTail: C[T]): C[H :: T]

403

def emptyProduct: C[HNil]

404

def derive[F, G](instance: C[G], iso: Iso[F, G]): C[F]

405

}

406

```

407

408

**Usage Examples:**

409

410

```scala

411

import shapeless._

412

413

// Example: Show type class derivation

414

trait Show[T] {

415

def show(t: T): String

416

}

417

418

implicit val showInt: Show[Int] = _.toString

419

implicit val showString: Show[String] = identity

420

implicit val showBoolean: Show[Boolean] = _.toString

421

422

implicit val showHNil: Show[HNil] = _ => ""

423

implicit def showHList[H, T <: HList]

424

(implicit showH: Show[H], showT: Show[T]): Show[H :: T] =

425

hlist => s"${showH.show(hlist.head)} :: ${showT.show(hlist.tail)}"

426

427

val hlist = 42 :: "hello" :: true :: HNil

428

val shown = implicitly[Show[Int :: String :: Boolean :: HNil]].show(hlist)

429

// "42 :: hello :: true :: "

430

```

431

432

These generic programming utilities provide the foundation for building sophisticated abstractions while maintaining type safety and enabling automatic derivation of type class instances.