Type Name Utilities
The hearth.typename package provides TypeName[A] — a type class that produces human-readable type names at runtime.
Reference Implementation
Like Source Utilities, TypeName[A] is both a useful utility and a reference
implementation showing how to build auto-derived type classes on top of Hearth's runtime-aware type printing.
The TypeName[A] Type Class
TypeName[A] provides 4 different ways to print the name of a type:
| Method | Example (Option[String]) |
Description |
|---|---|---|
prettyPrint |
"\u001b[...scala.Option[java.lang.String]\u001b[...]" |
ANSI colored, fully qualified names |
plainPrint |
"scala.Option[java.lang.String]" |
no ANSI, fully qualified names |
simplePrint |
"Option[String]" |
no ANSI, short names with type parameters |
shortPrint |
"Option" |
no ANSI, short name only, no type parameters |
Instances are auto-derived — no boilerplate needed:
Example
import hearth.typename.TypeName
// Scala 2 — TypeName is resolved via `implicit def`
// Scala 3 — TypeName is resolved via `inline given`
val tn = TypeName[Option[String]]
println(tn.plainPrint) // "scala.Option[java.lang.String]"
println(tn.simplePrint) // "Option[String]"
println(tn.shortPrint) // "Option"
Injection from Existing Instances
When a TypeName instance exists for a type argument, the derived instance uses it instead of the default printing.
This allows you to customize how types appear inside larger type expressions:
Example
Context Bound Injection
The injection pattern is especially useful with context bounds — when deriving a TypeName for a type with
abstract type parameters, existing TypeName instances for those parameters are injected automatically:
Example
Scala Version Requirements for Injection
Injection from existing TypeName instances requires:
- Scala 2.13.17+ (uses
c.inferImplicitValueIgnoring) - Scala 3.7.0+ (uses
Implicits.searchIgnoring)
On older Scala versions (2.13.16, 3.3.x–3.6.x), TypeName still works correctly for basic
derivation (all 4 print methods produce accurate output), but injection is disabled — existing
TypeName instances for type arguments are not picked up. The macro falls back to default
type printing for all type arguments.
This limitation exists because safe implicit summoning (that avoids infinite recursion with the
auto-derived TypeName.derived) requires summonImplicitIgnoring, which is only available on
newer compiler versions.
Use Cases
1. Error Messages
Example
2. Debug Logging
3. Type Class Derivation Libraries
Example
TypeName[A] can be used inside macro-derived type class instances to produce better error messages:
How It Works
TypeName[A] is derived using a macro that:
- Calls
Type.runtimePrettyPrint,Type.runtimePlainPrint, andType.runtimeShortPrintto build the 4 print variants - For each type argument, attempts to summon an existing
TypeNameinstance (usingExpr.summonImplicitIgnoringto avoid recursion with the auto-derivedTypeName.derived) - If found, caches the summoned instance as a
lazy valand calls the corresponding method on it - Constructs an anonymous
TypeName[A]with the 4 computed strings
See TypeNameMacros.scala
for the shared macro implementation.
Relationship with Other Utilities
- Basic Utilities:
TypeNameis built onType.runtimePlainPrint,Type.runtimePrettyPrint, andType.runtimeShortPrint - Better Printers: The runtime-aware type printing infrastructure lives in Better Printers
- Source Utilities: Similar auto-derivation pattern (
implicit def/inline givenin companion)