Type Class
Derivation in
Scala 3
by José Luis Pintado Barbero
/jospint
Type classes
Type classes are “Type system constructs that support ad hoc polymorphism”.
In simpler terms:
● Defining behaviour for a group of types, without modifying those types, no inheritance.
● Custom implementation for each type.
● Generate instances that uses those implementations.
More specifically:
● A trait that defines the operations.
● Implicit instances that provide implementations for specific types.
● Methods that use the declared implicit instances.
Type classes in Scala 2
trait Encoder[E] {
def encode(value: E): String
}
implicit val booleanEncoder: Encoder[Boolean] = new Encoder[Boolean] {
def encode(value: Boolean): String = if (value) "yes" else "no"
}
def display[E](value: E)(implicit encoder: Encoder[E]): Unit =
println(encoder.encoder(value))
Type classes in Scala 3
trait Encoder[E]:
def encode(value: E): String
given Encoder[Boolean] with
def encode(value: Boolean): String = if (value) "yes" else "no"
def display[E](value: E)(using encoder: Encoder[E]): Unit =
println(encoder.encode(value))
val amIHuman = true
display(amIHuman) // yes
Defining more encoders with given/using clauses
given Encoder[Boolean] with
def encode(element: Boolean): String = if (element) "yes" else "no"
given Encoder[String] with
def encode(element: String): String = element
given Encoder[Int] with
def encode(element: Int): String = element.toString
Deriving given/using clauses
given encodeList[E](using encoder: Encoder[E]): Encoder[List[E]] with
def encode(element: List[E]): String =
element.map(encoder.encode).mkString("[", ", ", "]")
given encodeOption[E](using encoder: Encoder[E]): Encoder[Option[E]] with
def encode(element: Option[E]): String =
element match { case Some(e) => encoder.encode(e) case None => "None" }
Defining even more encoders… ?
case class Car(brand: String, year: Int, isNew: Boolean)
given encodePerson(using s: Encoder[String], i: Encoder[Int], b: Encoder[Boolean]
): Encoder[Car] with
def encode(element: Car): String = {
element match
case Car(brand, year, isNew) => {
List("brand: " + s.encode(brand), "year: " + i.encode(year),
"isNew: " + b.encode(isNew)
).mkString("{", ", ", "}")
}
}
display(Car("Toyota", 1997, false)) // {brand: Toyota, year: 1997, isNew: no}
Can we save ourselves from this boilerplate nightmare?
Yes
Type class derivation
● Automatically generate given instances for type classes which satisfy some simple
conditions.
● Keyword in Scala: derives.
● Implementation using derived method.
case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder
inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ???
Goal for this talk
case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder
inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ???
● We want to able to derive any case classes and enums automatically deriving Encoder!
● But first… let’s refresh/introduce some concepts before that…
Summoning instances
● summon Keyword used to requests given values.
● Similar to implicitly.
● summon can return a more precise type than the type it was asked for, compared to
implicitly.
Summoning instances
def display[E](value: E)(using encoder: Encoder[E]) =
println(encoder.encode(value))
def displayWithSummon[E](value: E)(using Encoder[E]) =
println(summon[Encoder[E]].encode(value))
def displayWithSummonAlternative[E: Encoder](value: E) =
println(summon[Encoder[E]].encode(value))
Inlines
● Marking a method definition as inline will, at compile time, copy the right hand side of a
method at the place where is used, instead of invoking it.
● One can have inline arguments, expanding the variable without computing it first.
● You can have conditional inlines and match inlines
● Usages:
○ Performance optimizations.
○ Information that inlines surfaces at compile time.
Inlines example
inline def inlineSquare(x: Int): Int = x * x
def normalSquare(x: Int): Int = x * x
val a = InlinesDemo.inlineSquare(5)
val b = InlinesDemo.normalSquare(5)
val a: Int = 25:Int
val b: Int = com.dixa.meetup.InlinesDemo.normalSquare(5)
Inlines example
inline def inlinedLog(inline level: String, message:
String): Unit =
inline if level == "debug" then
println(s"[DEBUG] $message")
else if level == "info" then
println(s"[INFO] $message")
else println(s"[UNKNOWN] $message")
def regularLog(level: String, message: String): Unit =
if level == "debug" then
println(s"[DEBUG] $message")
else if level == "info" then
println(s"[INFO] $message")
else println(s"[UNKNOWN] $message")
{
println(
_root_.scala.StringContext.apply(["[DEBUG] ","" :
String]*).s(
["Something happened!" : Any]*)
)
}:Unit
com.dixa.meetup.InlinesDemo.regularLog("debug",
"Something happened!")
Compile-time operations (scala.compiletime)
Helper definitions that provide support for compile-time operations over values
● summonInline
○ Delays summoning to the call site of the function, when the type will be concrete to
the compiler so it can know whether is possible to summon or not.
○ Used in conjunction with inline methods.
summonInline example
def display[E](value: E)(using encoder: Encoder[E]) = {
println(encoder.encode(value)) // It works!
}
def displaySummonDoesNotCompile[E](value: E) = {
println(summon[Encoder[E]].encode(value))
// No given instance of type com.dixa.meetup.Encoder[E] was found...
}
inline def displaySummonInline[E](value: E) ={
println(summonInline[Encoder[E]].encode(value)) // It works!
}
Compile-time operations (scala.compiletime)
Helper definitions that provide support for compile-time operations over values
● summonInline
○ Delays summoning to the call site of the function, when the type will be concrete to
the compiler so it can know whether is possible to summon or not.
○ Used in conjunction with inline methods.
● constValue
○ Produces the constant value represented by a type.
○ Needs to be constant!
constValue example
inline def printSize[N <: Int]: Unit = println(s"Size is ${constValue[N]}")
printSize[5] // 5
printSize[Int] // Int is not a constant type; cannot take constValue
Compile-time operations (scala.compiletime)
Helper definitions that provide support for compile-time operations over values
● summonInline
○ Delays summoning to the call site of the function, when the type will be concrete to
the compiler so it can know whether is possible to summon or not.
○ Used in conjunction with inline methods.
● constValue
○ Produces the constant value represented by a type.
○ Needs to be constant!
● erasedValue
○ Type erasure is a process where the compiler removes type information during
compilation. In the JVM, generic type parameters are erased at runtime.
○ erasedValue returns a “fake” literal value that can be used for inline matching
○ It cannot be be used at runtime, only at compile time!
erasedValue example
inline def describe[T]: String = inline erasedValue[T] match {
case _: Int => "This is an Int"
case _: String => "This is a String"
case _: List[?] => "This is a List"
case _ => "Unknown type"
}
val intDesc = describe[Int] // "This is an Int"
val strDesc = describe[String] // "This is a String"
val listDesc = describe[List[Int]] // "This is a List"
Mirrors
● Mirrors provides type level information about:
○ Product types: Case classes.
○ Sum types: Sealed traits, enums.
Mirrors
case class Person(name: String, age: Int)
val mirror = summon[Mirror.ProductOf[Person]]
type Labels = mirror.MirroredElemLabels // ("name", "age")
type Types = mirror.MirroredElemTypes // (String, Int)
val label = constValue[mirror.MirroredLabel] // "Person"
val jose: Person = mirror.fromTuple(("José", 37))
val aTuple: (String, Int) = Tuple.fromProductTyped(jose) // ("José", 37)
Type class derivation
● Automatically generate given instances for type classes which satisfy some simple
conditions.
● Keyword in Scala: derives.
● Implementation using derived method.
case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder
inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ???
● Goal: being able to derive case classes and enums automatically deriving Encoder!
Live Demo
Real life applications
● PureConfig
import pureconfig._
import pureconfig.generic.derivation.default._
import
pureconfig.generic.derivation.EnumConfigReader
sealed trait AnimalConf derives ConfigReader
case class DogConf(age: Int) extends AnimalConf
case class BirdConf(canFly: Boolean) extends
AnimalConf
ConfigSource.string("{ type: dog-conf, age: 4
}").load[AnimalConf]
import pureconfig.generic.derivation.EnumConfigReader
enum Season derives EnumConfigReader {
case Spring, Summer, Autumn, Winter
}
case class MyConf(list: List[Season]) derives ConfigReader
ConfigSource.string("{ list: [spring, summer, autumn,
winter] }").load[MyConf]
Real life applications
● uPickle
case class Dog(name: String, age: Int) derives ReadWriter
upickle.default.write(Dog("Ball", 2)) // """{"name":"Ball","age":2}"""
upickle.default.read[Dog]("""{"name":"Ball","age":2}""") // Dog("Ball", 2)
sealed trait Animal derives ReadWriter
case class Person(name: String, address: String, age: Int = 20) extends Animal
upickle.default.write(Person("Peter", "Ave 10"))
// """{"$type":"Person","name":"Peter","address":"Ave 10"}"""
upickle.default.read[Animal]("""{"$type":"Person","name":"Peter","address":"Ave 10"}""")
// Person("Peter", "Ave 10")
Further material
● Code for the presentation
https://github.com/jospint/scala-3-type-class-derivation
● RockTheJVM: Scala Macros and Metaprogramming
https://rockthejvm.com/courses/scala-macros-and-metaprogramming
● RockTheJVM: Type classes
https://www.youtube.com/watch?v=bupBZKJT0EA
● Riskified Tech: Type class Derivation in Scala 3
https://medium.com/riskified-technology/type-class-derivation-in-scala-3-ba3c7c41d3ef
● Scala 3 Reference: Type class Derivation
https://docs.scala-lang.org/scala3/reference/contextual/derivation.html
Thanks!

More Related Content

PDF
Kotlin: A pragmatic language by JetBrains
PDF
More expressive types for spark with frameless
PPTX
Code is not text! How graph technologies can help us to understand our code b...
PDF
Fantom - Programming Language for JVM, CLR, and Javascript
PDF
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
PPTX
Notes(1).pptx
PPTX
Scala fundamentals
PPTX
classes object fgfhdfgfdgfgfgfgfdoop.pptx
Kotlin: A pragmatic language by JetBrains
More expressive types for spark with frameless
Code is not text! How graph technologies can help us to understand our code b...
Fantom - Programming Language for JVM, CLR, and Javascript
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Notes(1).pptx
Scala fundamentals
classes object fgfhdfgfdgfgfgfgfdoop.pptx

Similar to Type Class Derivation in Scala 3 - Jose Luis Pintado Barbero (20)

PPTX
Kotlin as a Better Java
PDF
Reflection in Go
PPTX
TypeScript Presentation - Jason Haffey
PPT
Introduction to C#
PDF
Kotlin for Android devs
PPTX
c#(loops,arrays)
PPTX
Type script - advanced usage and practices
PDF
Back to the Future with TypeScript
ZIP
PDF
ADG Poznań - Kotlin for Android developers
PDF
C# Summer course - Lecture 2
PPTX
Typescript language extension of java script
PDF
Oh Crap, I Forgot (Or Never Learned) C! [CodeMash 2010]
PPTX
A well-typed program never goes wrong
PPT
Synapseindia dot net development
PPTX
Intro to object oriented programming
PPTX
Type Driven Development with TypeScript
PPSX
DITEC - Programming with C#.NET
PPTX
Python crush course
PDF
C programing Tutorial
Kotlin as a Better Java
Reflection in Go
TypeScript Presentation - Jason Haffey
Introduction to C#
Kotlin for Android devs
c#(loops,arrays)
Type script - advanced usage and practices
Back to the Future with TypeScript
ADG Poznań - Kotlin for Android developers
C# Summer course - Lecture 2
Typescript language extension of java script
Oh Crap, I Forgot (Or Never Learned) C! [CodeMash 2010]
A well-typed program never goes wrong
Synapseindia dot net development
Intro to object oriented programming
Type Driven Development with TypeScript
DITEC - Programming with C#.NET
Python crush course
C programing Tutorial
Ad

Recently uploaded (20)

DOCX
Modern SharePoint Intranet Templates That Boost Employee Engagement in 2025.docx
DOCX
How to Use SharePoint as an ISO-Compliant Document Management System
PDF
novaPDF Pro 11.9.482 Crack + License Key [Latest 2025]
PPTX
Trending Python Topics for Data Visualization in 2025
PDF
AI Guide for Business Growth - Arna Softech
PDF
Top 10 Software Development Trends to Watch in 2025 🚀.pdf
PPTX
most interesting chapter in the world ppt
PDF
The Dynamic Duo Transforming Financial Accounting Systems Through Modern Expe...
PPTX
Cybersecurity: Protecting the Digital World
PPTX
GSA Content Generator Crack (2025 Latest)
DOC
UTEP毕业证学历认证,宾夕法尼亚克拉里恩大学毕业证未毕业
PPTX
Computer Software - Technology and Livelihood Education
PDF
Practical Indispensable Project Management Tips for Delivering Successful Exp...
PDF
iTop VPN Crack Latest Version Full Key 2025
PDF
Guide to Food Delivery App Development.pdf
PPTX
Cybersecurity-and-Fraud-Protecting-Your-Digital-Life.pptx
PPTX
How to Odoo 19 Installation on Ubuntu - CandidRoot
PDF
How AI/LLM recommend to you ? GDG meetup 16 Aug by Fariman Guliev
PPTX
Python is a high-level, interpreted programming language
PDF
MCP Security Tutorial - Beginner to Advanced
Modern SharePoint Intranet Templates That Boost Employee Engagement in 2025.docx
How to Use SharePoint as an ISO-Compliant Document Management System
novaPDF Pro 11.9.482 Crack + License Key [Latest 2025]
Trending Python Topics for Data Visualization in 2025
AI Guide for Business Growth - Arna Softech
Top 10 Software Development Trends to Watch in 2025 🚀.pdf
most interesting chapter in the world ppt
The Dynamic Duo Transforming Financial Accounting Systems Through Modern Expe...
Cybersecurity: Protecting the Digital World
GSA Content Generator Crack (2025 Latest)
UTEP毕业证学历认证,宾夕法尼亚克拉里恩大学毕业证未毕业
Computer Software - Technology and Livelihood Education
Practical Indispensable Project Management Tips for Delivering Successful Exp...
iTop VPN Crack Latest Version Full Key 2025
Guide to Food Delivery App Development.pdf
Cybersecurity-and-Fraud-Protecting-Your-Digital-Life.pptx
How to Odoo 19 Installation on Ubuntu - CandidRoot
How AI/LLM recommend to you ? GDG meetup 16 Aug by Fariman Guliev
Python is a high-level, interpreted programming language
MCP Security Tutorial - Beginner to Advanced
Ad

Type Class Derivation in Scala 3 - Jose Luis Pintado Barbero

  • 1. Type Class Derivation in Scala 3 by José Luis Pintado Barbero /jospint
  • 2. Type classes Type classes are “Type system constructs that support ad hoc polymorphism”. In simpler terms: ● Defining behaviour for a group of types, without modifying those types, no inheritance. ● Custom implementation for each type. ● Generate instances that uses those implementations. More specifically: ● A trait that defines the operations. ● Implicit instances that provide implementations for specific types. ● Methods that use the declared implicit instances.
  • 3. Type classes in Scala 2 trait Encoder[E] { def encode(value: E): String } implicit val booleanEncoder: Encoder[Boolean] = new Encoder[Boolean] { def encode(value: Boolean): String = if (value) "yes" else "no" } def display[E](value: E)(implicit encoder: Encoder[E]): Unit = println(encoder.encoder(value))
  • 4. Type classes in Scala 3 trait Encoder[E]: def encode(value: E): String given Encoder[Boolean] with def encode(value: Boolean): String = if (value) "yes" else "no" def display[E](value: E)(using encoder: Encoder[E]): Unit = println(encoder.encode(value)) val amIHuman = true display(amIHuman) // yes
  • 5. Defining more encoders with given/using clauses given Encoder[Boolean] with def encode(element: Boolean): String = if (element) "yes" else "no" given Encoder[String] with def encode(element: String): String = element given Encoder[Int] with def encode(element: Int): String = element.toString
  • 6. Deriving given/using clauses given encodeList[E](using encoder: Encoder[E]): Encoder[List[E]] with def encode(element: List[E]): String = element.map(encoder.encode).mkString("[", ", ", "]") given encodeOption[E](using encoder: Encoder[E]): Encoder[Option[E]] with def encode(element: Option[E]): String = element match { case Some(e) => encoder.encode(e) case None => "None" }
  • 7. Defining even more encoders… ? case class Car(brand: String, year: Int, isNew: Boolean) given encodePerson(using s: Encoder[String], i: Encoder[Int], b: Encoder[Boolean] ): Encoder[Car] with def encode(element: Car): String = { element match case Car(brand, year, isNew) => { List("brand: " + s.encode(brand), "year: " + i.encode(year), "isNew: " + b.encode(isNew) ).mkString("{", ", ", "}") } } display(Car("Toyota", 1997, false)) // {brand: Toyota, year: 1997, isNew: no}
  • 8. Can we save ourselves from this boilerplate nightmare? Yes
  • 9. Type class derivation ● Automatically generate given instances for type classes which satisfy some simple conditions. ● Keyword in Scala: derives. ● Implementation using derived method. case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ???
  • 10. Goal for this talk case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ??? ● We want to able to derive any case classes and enums automatically deriving Encoder! ● But first… let’s refresh/introduce some concepts before that…
  • 11. Summoning instances ● summon Keyword used to requests given values. ● Similar to implicitly. ● summon can return a more precise type than the type it was asked for, compared to implicitly.
  • 12. Summoning instances def display[E](value: E)(using encoder: Encoder[E]) = println(encoder.encode(value)) def displayWithSummon[E](value: E)(using Encoder[E]) = println(summon[Encoder[E]].encode(value)) def displayWithSummonAlternative[E: Encoder](value: E) = println(summon[Encoder[E]].encode(value))
  • 13. Inlines ● Marking a method definition as inline will, at compile time, copy the right hand side of a method at the place where is used, instead of invoking it. ● One can have inline arguments, expanding the variable without computing it first. ● You can have conditional inlines and match inlines ● Usages: ○ Performance optimizations. ○ Information that inlines surfaces at compile time.
  • 14. Inlines example inline def inlineSquare(x: Int): Int = x * x def normalSquare(x: Int): Int = x * x val a = InlinesDemo.inlineSquare(5) val b = InlinesDemo.normalSquare(5) val a: Int = 25:Int val b: Int = com.dixa.meetup.InlinesDemo.normalSquare(5)
  • 15. Inlines example inline def inlinedLog(inline level: String, message: String): Unit = inline if level == "debug" then println(s"[DEBUG] $message") else if level == "info" then println(s"[INFO] $message") else println(s"[UNKNOWN] $message") def regularLog(level: String, message: String): Unit = if level == "debug" then println(s"[DEBUG] $message") else if level == "info" then println(s"[INFO] $message") else println(s"[UNKNOWN] $message") { println( _root_.scala.StringContext.apply(["[DEBUG] ","" : String]*).s( ["Something happened!" : Any]*) ) }:Unit com.dixa.meetup.InlinesDemo.regularLog("debug", "Something happened!")
  • 16. Compile-time operations (scala.compiletime) Helper definitions that provide support for compile-time operations over values ● summonInline ○ Delays summoning to the call site of the function, when the type will be concrete to the compiler so it can know whether is possible to summon or not. ○ Used in conjunction with inline methods.
  • 17. summonInline example def display[E](value: E)(using encoder: Encoder[E]) = { println(encoder.encode(value)) // It works! } def displaySummonDoesNotCompile[E](value: E) = { println(summon[Encoder[E]].encode(value)) // No given instance of type com.dixa.meetup.Encoder[E] was found... } inline def displaySummonInline[E](value: E) ={ println(summonInline[Encoder[E]].encode(value)) // It works! }
  • 18. Compile-time operations (scala.compiletime) Helper definitions that provide support for compile-time operations over values ● summonInline ○ Delays summoning to the call site of the function, when the type will be concrete to the compiler so it can know whether is possible to summon or not. ○ Used in conjunction with inline methods. ● constValue ○ Produces the constant value represented by a type. ○ Needs to be constant!
  • 19. constValue example inline def printSize[N <: Int]: Unit = println(s"Size is ${constValue[N]}") printSize[5] // 5 printSize[Int] // Int is not a constant type; cannot take constValue
  • 20. Compile-time operations (scala.compiletime) Helper definitions that provide support for compile-time operations over values ● summonInline ○ Delays summoning to the call site of the function, when the type will be concrete to the compiler so it can know whether is possible to summon or not. ○ Used in conjunction with inline methods. ● constValue ○ Produces the constant value represented by a type. ○ Needs to be constant! ● erasedValue ○ Type erasure is a process where the compiler removes type information during compilation. In the JVM, generic type parameters are erased at runtime. ○ erasedValue returns a “fake” literal value that can be used for inline matching ○ It cannot be be used at runtime, only at compile time!
  • 21. erasedValue example inline def describe[T]: String = inline erasedValue[T] match { case _: Int => "This is an Int" case _: String => "This is a String" case _: List[?] => "This is a List" case _ => "Unknown type" } val intDesc = describe[Int] // "This is an Int" val strDesc = describe[String] // "This is a String" val listDesc = describe[List[Int]] // "This is a List"
  • 22. Mirrors ● Mirrors provides type level information about: ○ Product types: Case classes. ○ Sum types: Sealed traits, enums.
  • 23. Mirrors case class Person(name: String, age: Int) val mirror = summon[Mirror.ProductOf[Person]] type Labels = mirror.MirroredElemLabels // ("name", "age") type Types = mirror.MirroredElemTypes // (String, Int) val label = constValue[mirror.MirroredLabel] // "Person" val jose: Person = mirror.fromTuple(("José", 37)) val aTuple: (String, Int) = Tuple.fromProductTyped(jose) // ("José", 37)
  • 24. Type class derivation ● Automatically generate given instances for type classes which satisfy some simple conditions. ● Keyword in Scala: derives. ● Implementation using derived method. case class Car(brand: String, year: Int, isNew: Boolean) derives Encoder inline def derived[E](using m: Mirror.Of[E]): Encoder[E] = ??? ● Goal: being able to derive case classes and enums automatically deriving Encoder!
  • 26. Real life applications ● PureConfig import pureconfig._ import pureconfig.generic.derivation.default._ import pureconfig.generic.derivation.EnumConfigReader sealed trait AnimalConf derives ConfigReader case class DogConf(age: Int) extends AnimalConf case class BirdConf(canFly: Boolean) extends AnimalConf ConfigSource.string("{ type: dog-conf, age: 4 }").load[AnimalConf] import pureconfig.generic.derivation.EnumConfigReader enum Season derives EnumConfigReader { case Spring, Summer, Autumn, Winter } case class MyConf(list: List[Season]) derives ConfigReader ConfigSource.string("{ list: [spring, summer, autumn, winter] }").load[MyConf]
  • 27. Real life applications ● uPickle case class Dog(name: String, age: Int) derives ReadWriter upickle.default.write(Dog("Ball", 2)) // """{"name":"Ball","age":2}""" upickle.default.read[Dog]("""{"name":"Ball","age":2}""") // Dog("Ball", 2) sealed trait Animal derives ReadWriter case class Person(name: String, address: String, age: Int = 20) extends Animal upickle.default.write(Person("Peter", "Ave 10")) // """{"$type":"Person","name":"Peter","address":"Ave 10"}""" upickle.default.read[Animal]("""{"$type":"Person","name":"Peter","address":"Ave 10"}""") // Person("Peter", "Ave 10")
  • 28. Further material ● Code for the presentation https://github.com/jospint/scala-3-type-class-derivation ● RockTheJVM: Scala Macros and Metaprogramming https://rockthejvm.com/courses/scala-macros-and-metaprogramming ● RockTheJVM: Type classes https://www.youtube.com/watch?v=bupBZKJT0EA ● Riskified Tech: Type class Derivation in Scala 3 https://medium.com/riskified-technology/type-class-derivation-in-scala-3-ba3c7c41d3ef ● Scala 3 Reference: Type class Derivation https://docs.scala-lang.org/scala3/reference/contextual/derivation.html