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

typeable.mddocs/

0

# Runtime Type Safety

1

2

The `Typeable` type class in shapeless provides runtime type-safe casting with compile-time guarantees. It enables safe downcasting and type checking operations while maintaining the benefits of static typing.

3

4

## Core Type Class

5

6

### Typeable Definition

7

8

```scala { .api }

9

/**

10

* Supports runtime type-safe casting for type U

11

*/

12

trait Typeable[U] {

13

/**

14

* Attempt to cast Any value to type U

15

* @param t value to cast

16

* @return Some(u) if cast succeeds, None if it fails

17

*/

18

def cast(t: Any): Option[U]

19

}

20

```

21

22

## Enhanced Casting Operations

23

24

### Cast Enhancement

25

26

```scala { .api }

27

/**

28

* Provides type-safe casting operations for any value

29

*/

30

class Cast(t: Any) {

31

/**

32

* Attempt to cast to type U

33

*/

34

def cast[U](implicit castU: Typeable[U]): Option[U] = castU.cast(t)

35

}

36

37

/**

38

* Implicit conversion to enable .cast syntax on any value

39

*/

40

implicit def anyCast(t: Any): Cast = new Cast(t)

41

```

42

43

**Usage Examples:**

44

45

```scala

46

import shapeless._

47

48

val anyValue: Any = 42

49

val stringValue: Any = "hello"

50

val listValue: Any = List(1, 2, 3)

51

52

// Safe casting with Option results

53

val asInt: Option[Int] = anyValue.cast[Int] // Some(42)

54

val asString: Option[String] = anyValue.cast[String] // None

55

val asList: Option[List[Int]] = listValue.cast[List[Int]] // Some(List(1, 2, 3))

56

57

// Use in pattern matching

58

anyValue.cast[Int] match {

59

case Some(i) => println(s"Got integer: $i")

60

case None => println("Not an integer")

61

}

62

63

// Chain operations safely

64

val result = for {

65

str <- stringValue.cast[String]

66

if str.length > 3

67

} yield str.toUpperCase

68

// result: Option[String] = Some("HELLO")

69

```

70

71

## Provided Instances

72

73

### Primitive Types

74

75

Shapeless provides `Typeable` instances for all primitive types:

76

77

```scala { .api }

78

// Numeric primitives

79

implicit val byteTypeable: Typeable[Byte]

80

implicit val shortTypeable: Typeable[Short]

81

implicit val charTypeable: Typeable[Char]

82

implicit val intTypeable: Typeable[Int]

83

implicit val longTypeable: Typeable[Long]

84

implicit val floatTypeable: Typeable[Float]

85

implicit val doubleTypeable: Typeable[Double]

86

implicit val booleanTypeable: Typeable[Boolean]

87

implicit val unitTypeable: Typeable[Unit]

88

```

89

90

**Usage Examples:**

91

92

```scala

93

import shapeless._

94

95

val mixedValues: List[Any] = List(42, 3.14, true, "hello", 'c')

96

97

// Filter by type

98

val integers = mixedValues.flatMap(_.cast[Int]) // List(42)

99

val doubles = mixedValues.flatMap(_.cast[Double]) // List(3.14)

100

val booleans = mixedValues.flatMap(_.cast[Boolean]) // List(true)

101

val chars = mixedValues.flatMap(_.cast[Char]) // List('c')

102

103

// Type-safe processing

104

def processNumeric(value: Any): Option[Double] = {

105

value.cast[Int].map(_.toDouble)

106

.orElse(value.cast[Double])

107

.orElse(value.cast[Float].map(_.toDouble))

108

.orElse(value.cast[Long].map(_.toDouble))

109

}

110

111

val numericResults = mixedValues.flatMap(processNumeric)

112

// List(42.0, 3.14)

113

```

114

115

### Hierarchy Types

116

117

```scala { .api }

118

// Type hierarchy roots

119

implicit val anyValTypeable: Typeable[AnyVal]

120

implicit val anyRefTypeable: Typeable[AnyRef]

121

```

122

123

**Usage Examples:**

124

125

```scala

126

import shapeless._

127

128

val values: List[Any] = List(42, "hello", true, List(1, 2), 3.14)

129

130

// Separate value types from reference types

131

val valueTypes = values.flatMap(_.cast[AnyVal]) // List(42, true, 3.14)

132

val referenceTypes = values.flatMap(_.cast[AnyRef]) // List("hello", List(1, 2))

133

134

// Check type categories

135

def categorizeValue(value: Any): String = {

136

if (value.cast[AnyVal].isDefined) "Value type"

137

else if (value.cast[AnyRef].isDefined) "Reference type"

138

else "Unknown type"

139

}

140

141

val categories = values.map(categorizeValue)

142

// List("Value type", "Reference type", "Value type", "Reference type", "Value type")

143

```

144

145

### Generic Container Types

146

147

```scala { .api }

148

// Option types

149

implicit def optionTypeable[T: Typeable]: Typeable[Option[T]]

150

151

// Either types

152

implicit def eitherTypeable[A: Typeable, B: Typeable]: Typeable[Either[A, B]]

153

implicit def leftTypeable[A: Typeable, B: Typeable]: Typeable[Left[A, B]]

154

implicit def rightTypeable[A: Typeable, B: Typeable]: Typeable[Right[A, B]]

155

156

// Collection types

157

implicit def genTraversableTypeable[CC[X] <: GenTraversable[X], T: Typeable]: Typeable[CC[T]]

158

implicit def genMapTypeable[M[X, Y], T: Typeable, U: Typeable]: Typeable[M[T, U]]

159

```

160

161

**Usage Examples:**

162

163

```scala

164

import shapeless._

165

166

val containers: List[Any] = List(

167

Some(42),

168

None,

169

Left("error"),

170

Right(100),

171

List(1, 2, 3),

172

Map("a" -> 1, "b" -> 2)

173

)

174

175

// Cast to specific container types

176

val optInts = containers.flatMap(_.cast[Option[Int]]) // List(Some(42), None)

177

val eitherInts = containers.flatMap(_.cast[Either[String, Int]]) // List(Left("error"), Right(100))

178

val intLists = containers.flatMap(_.cast[List[Int]]) // List(List(1, 2, 3))

179

val stringIntMaps = containers.flatMap(_.cast[Map[String, Int]]) // List(Map("a" -> 1, "b" -> 2))

180

181

// Process containers safely

182

def processOption[T](value: Any): Option[String] = {

183

value.cast[Option[T]] match {

184

case Some(Some(t)) => Some(s"Some($t)")

185

case Some(None) => Some("None")

186

case None => None

187

}

188

}

189

190

val optionResults = containers.flatMap(processOption[Int])

191

// List("Some(42)", "None")

192

```

193

194

### HList Types

195

196

```scala { .api }

197

// HNil and HList cons

198

implicit val hnilTypeable: Typeable[HNil]

199

implicit def hlistTypeable[H: Typeable, T <: HList : Typeable]: Typeable[H :: T]

200

```

201

202

**Usage Examples:**

203

204

```scala

205

import shapeless._

206

207

val hlists: List[Any] = List(

208

HNil,

209

42 :: HNil,

210

"hello" :: true :: HNil,

211

1 :: "test" :: false :: 3.14 :: HNil

212

)

213

214

// Cast to specific HList types

215

val emptyHLists = hlists.flatMap(_.cast[HNil]) // List(HNil)

216

val intHLists = hlists.flatMap(_.cast[Int :: HNil]) // List(42 :: HNil)

217

val stringBoolHLists = hlists.flatMap(_.cast[String :: Boolean :: HNil]) // List("hello" :: true :: HNil)

218

219

// Process HLists with known structure

220

def processStringBoolHList(value: Any): Option[String] = {

221

value.cast[String :: Boolean :: HNil].map { hlist =>

222

val str = hlist.head

223

val bool = hlist.tail.head

224

s"String: $str, Boolean: $bool"

225

}

226

}

227

228

val hlistResults = hlists.flatMap(processStringBoolHList)

229

// List("String: hello, Boolean: true")

230

```

231

232

### Default Instance

233

234

```scala { .api }

235

/**

236

* Default Typeable instance using ClassTag for reflection-based casting

237

*/

238

implicit def dfltTypeable[U](implicit mU: ClassTag[U]): Typeable[U] =

239

new Typeable[U] {

240

def cast(t: Any): Option[U] =

241

if (mU.runtimeClass.isInstance(t)) Some(t.asInstanceOf[U]) else None

242

}

243

```

244

245

This provides `Typeable` instances for any type that has a `ClassTag`, covering most user-defined types.

246

247

**Usage Examples:**

248

249

```scala

250

import shapeless._

251

252

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

253

case class Product(name: String, price: Double)

254

255

val objects: List[Any] = List(

256

Person("Alice", 30),

257

Product("Widget", 19.99),

258

"not a case class",

259

42

260

)

261

262

// Cast to case class types (uses default Typeable via ClassTag)

263

val people = objects.flatMap(_.cast[Person]) // List(Person("Alice", 30))

264

val products = objects.flatMap(_.cast[Product]) // List(Product("Widget", 19.99))

265

266

// Type-safe extraction and processing

267

def processPerson(value: Any): Option[String] = {

268

value.cast[Person].map(p => s"${p.name} is ${p.age} years old")

269

}

270

271

def processProduct(value: Any): Option[String] = {

272

value.cast[Product].map(p => s"${p.name} costs $${p.price}")

273

}

274

275

val descriptions = objects.flatMap(obj =>

276

processPerson(obj).orElse(processProduct(obj))

277

)

278

// List("Alice is 30 years old", "Widget costs $19.99")

279

```

280

281

## Advanced Usage Patterns

282

283

### Type-safe Heterogeneous Collections

284

285

```scala

286

import shapeless._

287

288

// Type-safe heterogeneous storage

289

class TypedMap {

290

private var storage = Map[String, Any]()

291

292

def put[T: Typeable](key: String, value: T): Unit = {

293

storage = storage + (key -> value)

294

}

295

296

def get[T: Typeable](key: String): Option[T] = {

297

storage.get(key).flatMap(_.cast[T])

298

}

299

300

def getAll[T: Typeable]: List[T] = {

301

storage.values.flatMap(_.cast[T]).toList

302

}

303

}

304

305

val map = new TypedMap

306

map.put("name", "Alice")

307

map.put("age", 30)

308

map.put("active", true)

309

map.put("score", 95.5)

310

311

val name: Option[String] = map.get[String]("name") // Some("Alice")

312

val age: Option[Int] = map.get[Int]("age") // Some(30)

313

val wrong: Option[String] = map.get[String]("age") // None

314

315

val allStrings = map.getAll[String] // List("Alice")

316

val allNumbers = map.getAll[Double] // List(95.5)

317

```

318

319

### Runtime Type Validation

320

321

```scala

322

import shapeless._

323

324

// Validate JSON-like structures

325

def validateStructure(data: Any): Either[String, (String, Int, Boolean)] = {

326

data.cast[Map[String, Any]] match {

327

case Some(map) =>

328

for {

329

name <- map.get("name").flatMap(_.cast[String])

330

.toRight("Missing or invalid 'name' field")

331

age <- map.get("age").flatMap(_.cast[Int])

332

.toRight("Missing or invalid 'age' field")

333

active <- map.get("active").flatMap(_.cast[Boolean])

334

.toRight("Missing or invalid 'active' field")

335

} yield (name, age, active)

336

case None => Left("Expected Map structure")

337

}

338

}

339

340

val validData = Map("name" -> "Bob", "age" -> 25, "active" -> true)

341

val invalidData = Map("name" -> "Carol", "age" -> "twenty-five", "active" -> true)

342

343

val result1 = validateStructure(validData) // Right(("Bob", 25, true))

344

val result2 = validateStructure(invalidData) // Left("Missing or invalid 'age' field")

345

```

346

347

### Safe Deserialization

348

349

```scala

350

import shapeless._

351

352

// Type-safe deserialization helper

353

trait Deserializer[T] {

354

def deserialize(data: Any): Option[T]

355

}

356

357

implicit def typeableDeserializer[T: Typeable]: Deserializer[T] =

358

new Deserializer[T] {

359

def deserialize(data: Any): Option[T] = data.cast[T]

360

}

361

362

def safeDeserialize[T: Deserializer](data: Any): Option[T] =

363

implicitly[Deserializer[T]].deserialize(data)

364

365

// Usage with various types

366

val rawData: List[Any] = List(42, "hello", true, List(1, 2, 3))

367

368

val deserializedInt = rawData.flatMap(safeDeserialize[Int]) // List(42)

369

val deserializedString = rawData.flatMap(safeDeserialize[String]) // List("hello")

370

val deserializedList = rawData.flatMap(safeDeserialize[List[Int]]) // List(List(1, 2, 3))

371

```

372

373

The `Typeable` type class bridges the gap between compile-time and runtime type safety, enabling dynamic type checking while preserving the benefits of static typing. It's particularly useful for deserialization, dynamic dispatch, and working with heterogeneous data structures.