scalaadvanced
Type Class Pattern Implementation
Implement the type class pattern in Scala 3: define, provide instances, and use with extension methods.
scalaPress ⌘/Ctrl + Shift + C to copy
// Step 1: Define the type class
trait Eq[T]:
def eqv(a: T, b: T): Boolean
def neqv(a: T, b: T): Boolean = !eqv(a, b)
trait Printable[T]:
def format(value: T): String
trait Combinable[T]:
def empty: T
def combine(a: T, b: T): T
// Step 2: Provide instances
object Eq:
given Eq[Int] with
def eqv(a: Int, b: Int): Boolean = a == b
given Eq[String] with
def eqv(a: String, b: String): Boolean = a == b
given [A: Eq]: Eq[List[A]] with
def eqv(a: List[A], b: List[A]): Boolean =
a.length == b.length && a.zip(b).forall((x, y) => summon[Eq[A]].eqv(x, y))
object Printable:
given Printable[Int] with
def format(value: Int): String = value.toString
given Printable[String] with
def format(value: String): String = s"\"$value\""
given Printable[Boolean] with
def format(value: Boolean): String = if value then "yes" else "no"
object Combinable:
given Combinable[Int] with
def empty: Int = 0
def combine(a: Int, b: Int): Int = a + b
given Combinable[String] with
def empty: String = ""
def combine(a: String, b: String): String = a + b
given [A]: Combinable[List[A]] with
def empty: List[A] = Nil
def combine(a: List[A], b: List[A]): List[A] = a ++ b
// Step 3: Extension methods for ergonomic syntax
extension [T: Eq](a: T)
def ===(b: T): Boolean = summon[Eq[T]].eqv(a, b)
def =!=(b: T): Boolean = summon[Eq[T]].neqv(a, b)
extension [T: Printable](value: T)
def show: String = summon[Printable[T]].format(value)
extension [T: Combinable](a: T)
def |+|(b: T): T = summon[Combinable[T]].combine(a, b)
// Step 4: Generic algorithms using type classes
def combineAll[T: Combinable](items: List[T]): T =
val tc = summon[Combinable[T]]
items.foldLeft(tc.empty)(tc.combine)
def printAll[T: Printable](items: List[T]): Unit =
items.foreach(item => println(item.show))
// Custom data type with type class instances
case class Money(cents: Long):
override def toString: String = f"$$${cents / 100.0}%.2f"
given Eq[Money] with
def eqv(a: Money, b: Money): Boolean = a.cents == b.cents
given Printable[Money] with
def format(value: Money): String = value.toString
given Combinable[Money] with
def empty: Money = Money(0)
def combine(a: Money, b: Money): Money = Money(a.cents + b.cents)
@main def run(): Unit =
// Eq
println(1 === 1) // true
println(1 =!= 2) // true
println("a" === "a") // true
// Printable
println(42.show) // 42
println("hello".show) // "hello"
println(true.show) // yes
// Combinable
println(1 |+| 2) // 3
println("hello" |+| " world") // hello world
println(combineAll(List(1, 2, 3))) // 6
// Custom type
val prices = List(Money(999), Money(1499), Money(2999))
println(s"Total: ${combineAll(prices)}")
printAll(prices)Use Cases
- Ad-hoc polymorphism without inheritance
- Generic algorithms with type constraints
- Extensible behavior for existing types
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalabeginner
Collections Map Filter Fold Operations
Master Scala collections: map, flatMap, filter, fold, groupBy, partition, and zip operations.
Best for: Data transformation and aggregation
#scala#collections
scalaadvanced
Implicits and Givens Context Parameters
Use Scala 3 givens and using clauses for type classes, context parameters, and implicit conversions.
Best for: Type class pattern for ad-hoc polymorphism
#scala#givens
scalaintermediate
For-Comprehensions and Monadic Composition
Use for-comprehensions with Option, Either, Future, and custom monads for elegant composition.
Best for: Chaining optional computations
#scala#for-comprehension
scalaadvanced
Cats Effect IO Monad Basics
Use Cats Effect IO for pure functional effects: sequencing, error handling, resource management.
Best for: Pure functional effect management
#scala#cats-effect