or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-utilities.mdcoproduct-unions.mdgeneric-derivation.mdhlist-collections.mdindex.mdoptics-lenses.mdpoly-typelevel.mdrecords-fields.md

advanced-utilities.mddocs/

0

# Advanced Features and Utilities

1

2

Shapeless provides specialized features including functional cursors (zippers), tuple operations, compile-time testing utilities, annotation processing, runtime type information, and various conversion utilities for integration with Scala's standard library.

3

4

## Capabilities

5

6

### Zipper - Functional Cursors

7

8

Functional data structure for traversing and modifying HLists with positional context.

9

10

```scala { .api }

11

// Generic Zipper for any type with a representation via Generic

12

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

13

// Move cursor operations with type class constraints

14

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

15

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

16

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

17

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

18

19

// Move by n positions

20

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

21

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

22

23

// Modification operations

24

def put[A](a: A)(implicit put: Put[Self, A]): put.Out

25

def insert[A](a: A)(implicit insert: Insert[Self, A]): insert.Out

26

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

27

28

// Navigation in nested structures

29

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

30

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

31

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

32

}

33

34

// Zipper creation

35

trait HListZipper[L <: HList] {

36

type Out

37

def apply(l: L): Out

38

}

39

40

object Zipper {

41

// Create zipper focused on first element

42

def apply[L <: HList](l: L)(implicit zipper: HListZipper[L]): zipper.Out

43

}

44

```

45

46

### Zipper Operations

47

48

Type classes for zipper navigation and modification.

49

50

```scala { .api }

51

// Move cursor to the right (towards tail)

52

trait Right[Z] {

53

type Out

54

def apply(z: Z): Option[Out]

55

}

56

57

// Move cursor to the left (towards head)

58

trait Left[Z] {

59

type Out

60

def apply(z: Z): Option[Out]

61

}

62

63

// Move to first element

64

trait First[Z] {

65

type Out

66

def apply(z: Z): Out

67

}

68

69

// Move to last element

70

trait Last[Z] {

71

type Out

72

def apply(z: Z): Out

73

}

74

75

// Get element at cursor

76

trait Get[Z] {

77

type Out

78

def apply(z: Z): Out

79

}

80

81

// Replace element at cursor with A

82

trait Put[Z, A] {

83

type Out

84

def apply(z: Z, a: A): Out

85

}

86

87

// Insert element A at cursor

88

trait Insert[Z, A] {

89

type Out

90

def apply(z: Z, a: A): Out

91

}

92

93

// Delete element at cursor

94

trait Delete[Z] {

95

type Out

96

def apply(z: Z): Option[Out]

97

}

98

```

99

100

### Tuple Operations

101

102

Enhanced operations for working with Scala tuples through HList conversion.

103

104

```scala { .api }

105

// Witnesses tuple P can be decomposed

106

trait IsComposite[P] {

107

type H

108

type T

109

def head(p: P): H

110

def tail(p: P): T

111

}

112

113

// All but last element of tuple

114

trait Init[T] {

115

type Out

116

def apply(t: T): Out

117

}

118

119

// Last element of tuple

120

trait Last[T] {

121

type Out

122

def apply(t: T): Out

123

}

124

125

// Prepend element to tuple

126

trait Prepend[P, T] {

127

type Out

128

def apply(p: P, t: T): Out

129

}

130

131

// Reverse first tuple and prepend to second

132

trait ReversePrepend[P, Q] {

133

type Out

134

def apply(p: P, q: Q): Out

135

}

136

```

137

138

### Compile-Time Testing Utilities

139

140

Tools for compile-time verification and type checking.

141

142

```scala { .api }

143

// Assert expression has type T

144

def typed[T](t: => T): Unit = {}

145

146

// Assert two expressions have same type T

147

def sameTyped[T](t1: => T)(t2: => T): Unit = {}

148

149

// Display type T at compile time (compiler output)

150

def showType[T]: Unit = {}

151

152

// Display type bounds for T

153

def showBounds[T]: Unit = {}

154

155

// Assert code in string t fails to type check

156

def typeError(t: String): Unit = macro TestMacros.typeError

157

158

// Assert code in string t fails to compile

159

def compileError(t: String): Unit = macro TestMacros.compileError

160

161

// Deprecated alias for typeError

162

def illTyped(t: String): Unit = macro TestMacros.illTyped

163

164

// Usage:

165

typed[String]("hello") // Compiles

166

typed[Int]("hello") // Compile error

167

sameTyped(1)(2) // Compiles (both Int)

168

sameTyped("a")(1) // Compile error

169

typeError("val x: String = 123") // Succeeds (code doesn't type check)

170

```

171

172

### Runtime Type Information (Typeable)

173

174

Enhanced runtime type information beyond Scala's built-in TypeTag.

175

176

```scala { .api }

177

// Enhanced runtime type information

178

trait Typeable[T] {

179

// Safe casting with runtime type checking

180

def cast(any: Any): Option[T]

181

182

// Describe the type at runtime

183

def describe: String

184

185

// Type equality checking at runtime

186

def =:=[U](other: Typeable[U]): Boolean

187

}

188

189

object Typeable {

190

// Get Typeable instance

191

def apply[T](implicit typeable: Typeable[T]): Typeable[T] = typeable

192

193

// Built-in instances for primitive types

194

implicit val intTypeable: Typeable[Int]

195

implicit val stringTypeable: Typeable[String]

196

implicit val booleanTypeable: Typeable[Boolean]

197

198

// Generic derivation for case classes and sealed traits

199

implicit def genericTypeable[T](implicit

200

gen: Generic[T],

201

reprTypeable: Lazy[Typeable[gen.Repr]]

202

): Typeable[T]

203

}

204

```

205

206

### Annotation Support

207

208

Compile-time annotation access and processing.

209

210

```scala { .api }

211

// Extract annotation A from type T

212

trait Annotation[T, A] {

213

def apply(): A

214

}

215

216

// Extract all annotations A from type T

217

trait Annotations[T, A] {

218

def apply(): List[A]

219

}

220

221

object Annotation {

222

// Get annotation instance

223

def apply[T, A](implicit ann: Annotation[T, A]): A = ann()

224

}

225

226

// Usage with custom annotations:

227

class MyAnnotation(value: String) extends scala.annotation.StaticAnnotation

228

229

@MyAnnotation("example")

230

case class AnnotatedClass(field: String)

231

232

val annotation = Annotation[AnnotatedClass, MyAnnotation]

233

// Returns MyAnnotation("example")

234

```

235

236

### Default Value Derivation

237

238

Automatic derivation of default values for types.

239

240

```scala { .api }

241

// Provides default values for types T

242

trait Default[T] {

243

def apply(): T

244

}

245

246

object Default {

247

// Get default instance

248

def apply[T](implicit default: Default[T]): T = default()

249

250

// Built-in defaults

251

implicit val intDefault: Default[Int] = () => 0

252

implicit val stringDefault: Default[String] = () => ""

253

implicit val booleanDefault: Default[Boolean] = () => false

254

255

// Option defaults to None

256

implicit def optionDefault[T]: Default[Option[T]] = () => None

257

258

// List defaults to empty

259

implicit def listDefault[T]: Default[List[T]] = () => Nil

260

261

// Automatic derivation for case classes

262

implicit def genericDefault[A, L <: HList](implicit

263

gen: Generic.Aux[A, L],

264

defaults: Default[L]

265

): Default[A] = () => gen.from(defaults())

266

}

267

```

268

269

### Alternative Resolution (OrElse)

270

271

Alternative implicit resolution with fallback behavior.

272

273

```scala { .api }

274

// Try A, fallback to B if A not available

275

trait OrElse[A, B] {

276

type Out

277

def apply(): Out

278

}

279

280

object OrElse {

281

// Prefer primary if available

282

implicit def primary[A, B](implicit a: A): OrElse[A, B] =

283

new OrElse[A, B] {

284

type Out = A

285

def apply() = a

286

}

287

288

// Fallback to secondary

289

implicit def secondary[A, B](implicit b: B): OrElse[A, B] =

290

new OrElse[A, B] {

291

type Out = B

292

def apply() = b

293

}

294

}

295

296

// Usage for providing default implementations

297

trait Show[T] { def show(t: T): String }

298

trait PrettyShow[T] { def prettyShow(t: T): String }

299

300

def display[T](t: T)(implicit showable: OrElse[PrettyShow[T], Show[T]]): String = {

301

showable() match {

302

case pretty: PrettyShow[T] => pretty.prettyShow(t)

303

case show: Show[T] => show.show(t)

304

}

305

}

306

```

307

308

### Refutation - Negative Reasoning

309

310

Witnesses the absence of implicit instances for negative reasoning about types.

311

312

```scala { .api }

313

// Witnesses the absence of implicit instances of type T

314

trait Refute[T]

315

316

object Refute {

317

// Evidence that T is not available as implicit

318

implicit def refute[T](implicit dummy: Refute.Dummy): Refute[T] =

319

new Refute[T] {}

320

321

// Fails if T is available as implicit

322

implicit def ambiguous1[T](implicit t: T): Refute[T] = ???

323

implicit def ambiguous2[T](implicit t: T): Refute[T] = ???

324

325

trait Dummy

326

implicit val dummy: Dummy = new Dummy {}

327

}

328

329

// Usage:

330

def requireNoShow[T](implicit refute: Refute[Show[T]]): String =

331

"No Show instance available for T"

332

```

333

334

### Unwrapped Types

335

336

Automatic unwrapping of newtype/tagged wrapper types.

337

338

```scala { .api }

339

// Unwrapper from wrapper W to underlying U

340

trait Unwrapped[W] {

341

type U

342

def unwrap(w: W): U

343

}

344

345

object Unwrapped {

346

// Type alias with fixed underlying type

347

type Aux[W, U0] = Unwrapped[W] { type U = U0 }

348

349

// Unwrap tagged types

350

implicit def taggedUnwrapped[T, Tag]: Unwrapped.Aux[T @@ Tag, T] =

351

new Unwrapped[T @@ Tag] {

352

type U = T

353

def unwrap(tagged: T @@ Tag): T = tagged

354

}

355

}

356

```

357

358

### Standard Library Extensions

359

360

Extensions and integrations with Scala's standard library.

361

362

```scala { .api }

363

// Either extensions

364

implicit class EitherOps[A, B](either: Either[A, B]) {

365

def toHList: A :+: B :+: CNil = either match {

366

case Left(a) => Inl(a)

367

case Right(b) => Inr(Inl(b))

368

}

369

}

370

371

// Function extensions

372

implicit class FunctionOps[A, B](f: A => B) {

373

def toPolyTrain: Poly1 = new Poly1 {

374

implicit def cse = at[A](f)

375

}

376

}

377

378

// Map extensions

379

implicit class MapOps[K, V](map: Map[K, V]) {

380

def toRecord: HList = ??? // Convert to record representation

381

}

382

383

// Traversable extensions

384

implicit class TraversableOps[A](trav: Traversable[A]) {

385

def toHList: HList = ??? // Convert to HList if homogeneous

386

}

387

```

388

389

## Usage Examples

390

391

### Zipper Navigation

392

393

```scala

394

import shapeless._, zipper._

395

396

val data = "first" :: 2 :: true :: "last" :: HNil

397

398

// Create zipper focused on first element

399

val zipper = data.toZipper

400

// Zipper focused on "first"

401

402

// Navigate

403

val moved = zipper.right.right // Now focused on true

404

val atEnd = moved.right // Now focused on "last"

405

val backToFirst = atEnd.first // Back to "first"

406

407

// Modify at cursor

408

val modified = moved.put(false) // Replace true with false

409

val inserted = moved.insert("new") // Insert "new" before true

410

val deleted = moved.delete // Remove true

411

412

// Convert back to HList

413

val result = modified.toHList

414

// "first" :: 2 :: false :: "last" :: HNil

415

```

416

417

### Runtime Type Checking

418

419

```scala

420

import shapeless._, typeable._

421

422

def safeCast[T: Typeable](any: Any): Option[T] =

423

Typeable[T].cast(any)

424

425

// Safe casting with runtime checks

426

val value: Any = "hello world"

427

428

val asString = safeCast[String](value) // Some("hello world")

429

val asInt = safeCast[Int](value) // None

430

val asList = safeCast[List[String]](value) // None

431

432

// Type descriptions

433

val stringDesc = Typeable[String].describe // "String"

434

val listDesc = Typeable[List[Int]].describe // "List[Int]"

435

val complexDesc = Typeable[(String, Option[Int])].describe // "(String, Option[Int])"

436

```

437

438

### Compile-Time Testing

439

440

```scala

441

import shapeless._, test._

442

443

// Verify types at compile time

444

typed[String]("hello") // ✓ Compiles

445

typed[Int](42) // ✓ Compiles

446

// typed[String](42) // ✗ Compile error

447

448

// Verify same types

449

sameTyped(List(1, 2, 3))(List(4, 5, 6)) // ✓ Both List[Int]

450

// sameTyped(List(1, 2, 3))("hello") // ✗ Different types

451

452

// Verify code fails to compile

453

typeError("val x: String = 123") // ✓ Code doesn't type check

454

typeError("val y: Int = 456") // ✗ Code does type check

455

456

// Debug types at compile time

457

showType[List[String]] // Prints type to compiler output

458

showBounds[List[_]] // Shows type bounds

459

```

460

461

### Default Value Generation

462

463

```scala

464

import shapeless._, Default._

465

466

case class Config(

467

host: String,

468

port: Int,

469

ssl: Boolean,

470

timeout: Option[Int],

471

retries: List[Int]

472

)

473

474

// Automatic default derivation

475

implicit val hostDefault: Default[String] = () => "localhost"

476

implicit val portDefault: Default[Int] = () => 8080

477

implicit val sslDefault: Default[Boolean] = () => false

478

479

val defaultConfig = Default[Config].apply()

480

// Config("localhost", 8080, false, None, List())

481

482

// Manual defaults

483

case class Person(name: String, age: Int = 0, active: Boolean = true)

484

485

val defaultPerson = Default[Person].apply()

486

// Uses field defaults where available

487

```

488

489

### Annotation Processing

490

491

```scala

492

import shapeless._, annotation._

493

494

// Custom annotations

495

class ApiDoc(description: String) extends scala.annotation.StaticAnnotation

496

class Deprecated(since: String) extends scala.annotation.StaticAnnotation

497

498

@ApiDoc("User information")

499

case class User(

500

@ApiDoc("Full name") name: String,

501

@ApiDoc("Age in years") @Deprecated("2.0") age: Int,

502

@ApiDoc("Account status") active: Boolean

503

)

504

505

// Extract annotations

506

val classDoc = Annotation[User, ApiDoc]

507

// ApiDoc("User information")

508

509

val fieldDocs = Annotations[User, ApiDoc]

510

// List(ApiDoc("Full name"), ApiDoc("Age in years"), ApiDoc("Account status"))

511

512

val deprecations = Annotations[User, Deprecated]

513

// List(Deprecated("2.0"))

514

```

515

516

### Alternative Resolution

517

518

```scala

519

import shapeless._, OrElse._

520

521

// Preference system for formatting

522

trait JsonFormat[T] { def toJson(t: T): String }

523

trait SimpleFormat[T] { def format(t: T): String }

524

525

implicit val intJson: JsonFormat[Int] = i => s"""{"value": $i}"""

526

implicit val stringSimple: SimpleFormat[String] = s => s""""$s""""

527

528

def formatValue[T](t: T)(implicit

529

formatter: OrElse[JsonFormat[T], SimpleFormat[T]]

530

): String = {

531

formatter() match {

532

case json: JsonFormat[T] => json.toJson(t)

533

case simple: SimpleFormat[T] => simple.format(t)

534

}

535

}

536

537

val intFormatted = formatValue(42) // Uses JsonFormat: {"value": 42}

538

val stringFormatted = formatValue("hi") // Uses SimpleFormat: "hi"

539

```

540

541

### Unwrapping Tagged Types

542

543

```scala

544

import shapeless._, tag._, Unwrapped._

545

546

// Tagged types for domain modeling

547

trait UserId

548

trait Email

549

550

type UserIdValue = String @@ UserId

551

type EmailValue = String @@ Email

552

553

val userId: UserIdValue = tag[UserId]("user123")

554

val email: EmailValue = tag[Email]("test@example.com")

555

556

// Automatic unwrapping

557

def processId[T](wrapped: T)(implicit

558

unwrapped: Unwrapped[T]

559

): unwrapped.U = unwrapped.unwrap(wrapped)

560

561

val rawUserId: String = processId(userId) // "user123"

562

val rawEmail: String = processId(email) // "test@example.com"

563

564

// Type-safe unwrapping preserves information

565

def logValue[W](wrapped: W)(implicit

566

unwrapped: Unwrapped.Aux[W, String]

567

): Unit = println(s"Value: ${unwrapped.unwrap(wrapped)}")

568

569

logValue(userId) // "Value: user123"

570

logValue(email) // "Value: test@example.com"

571

```