Day 03 Applied Practice

Traits, Type Classes & Implicits

Scala's trait system enables flexible code composition. Type classes provide ad-hoc polymorphism. Given/using (Scala 3) and implicits (Scala 2) enable powerful context-passing patterns used throughout the ecosystem.

~1 hour Hands-on Precision AI Academy

Today’s Objective

Scala's trait system enables flexible code composition. Type classes provide ad-hoc polymorphism. Given/using (Scala 3) and implicits (Scala 2) enable powerful context-passing patterns used throughout the ecosystem.

Traits are like Java interfaces but can contain concrete implementations. A class can extend multiple traits (no diamond problem — linearization resolves conflicts). Traits can have abstract and concrete methods, fields, and even state. Self-type annotations (this: OtherTrait =>) declare that a trait requires another trait to be mixed in, enabling dependency injection at the type level.

Type Classes in Scala

A type class is a trait with a type parameter: 'trait Serializable[A] { def serialize(a: A): String }'. Instances are provided as given/implicit values: 'given Serializable[Int] = new Serializable[Int] { ... }'. Functions using type classes take an implicit/using parameter: 'def print[A](a: A)(using s: Serializable[A]) = s.serialize(a)'. The compiler automatically finds and passes the right instance.

Extension Methods and Scala 3

Scala 3 added extension methods as a first-class feature: 'extension (n: Int) def isEven: Boolean = n % 2 == 0'. This adds methods to existing types without subclassing. Scala 3 also replaced implicit with more explicit given/using syntax, making context passing clearer. Union types (Int | String), intersection types (A & B), and opaque type aliases are powerful new type system features.

scala
SCALA
// Type class pattern (Scala 3)
trait Show[A]: def show(a: A): String

given Show[Int] with def show(n: Int) = s'Int($n)'

given Show[String] with def show(s: String) = s'"$s"'

given [A: Show] => Show[List[A]] with def show(xs: List[A]) = xs.map(summon[Show[A]].show).mkString('[', ', ', ']')

def display[A](a: A)(using s: Show[A]): Unit = println(s.show(a))

display(42) // Int(42)
display(List(1, 2, 3)) // [Int(1), Int(2), Int(3)]

// Extension methods (Scala 3)
extension (s: String) def isPalindrome: Boolean = s == s.reverse def wordCount: Int = s.trim.split('\\s+').length

println('racecar'.isPalindrome) // true
println('Hello world foo'.wordCount) // 3
Use summon[TC[A]] in Scala 3 (the equivalent of implicitly in Scala 2) to retrieve an implicit/given instance explicitly. This is clearer than relying on the compiler to inject it invisibly.
📝 Day 3 Exercise Build a JSON Encoder Type Class
  1. Define a trait JsonEncodable[A] with a method toJson(a: A): String
  2. Provide given instances for Int, Double, String, Boolean, and List[A: JsonEncodable]
  3. Define a case class Person(name: String, age: Int) and provide its instance
  4. Write a function encode[A: JsonEncodable](a: A): String that uses the type class
  5. Test encoding a List[Person] — the compiler should find all instances automatically

Supporting Resources

Go deeper with these references.

Scala Docs
Official Scala Documentation Complete Scala language specification, standard library reference, and tutorials.
→
Coursera
Functional Programming in Scala Martin Odersky's course on functional programming principles.
→
Apache
Apache Spark Documentation Official docs for Spark 3.x with Scala and Python API references.
→

Day 3 Checkpoint

Before moving on, make sure you can answer these without looking:

Continue To Day 4
Futures, Akka & Concurrency
→