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