SlideShare a Scribd company logo
Generic Functional Programming
with Type Classes
Tapio Rautonen
Tapio Rautonen
@trautonen
De facto visibility optimization
technology for video game industry
and world leading 3D optimization and
delivery platform
Bending Scala and other technologies
to provide the most immersive 3D
experience from the cloud
The following presentation shows some raw
Scala code and might even discover the M-word.
In case of dizzy vision or fuzzy sounds in your
head, cover your eyes and ears and run.
The presenter is not responsible if the audience
turns functional programmers and introduce
Haskell to their next project.
Generic is not specific
trait ContactRepository {
def getContact(name: String): Future[Option[Contact]]
def updateContact(contact: Contact): Future[Contact]
}
trait Calculus {
def sum(x: Double, y: Double): Either[ArithmeticError, Double]
def divide(x: Double, y: Double): Either[ArithmeticError, Double]
def mean(xs: Iterable[Double]): Either[ArithmeticError, Double]
}
Functional programming is not object oriented
class MissileSilo(control: MissileControl) {
var missileCount: Int = 3
def launchMissile(): Unit = {
missileCount = missileCount - 1
control.ignite()
}
}
Type classes are not ad hoc inheritance
abstract class Animal {
var name: String = null
def utter(): Unit
override def equals(obj: Any): Boolean =
obj.isInstanceOf[Animal] && obj.asInstanceOf[Animal].name == name
}
class Dog extends Animal {
override def utter(): Unit = print("Wuf!")
override def equals(obj: Any): Boolean =
obj.isInstanceOf[Dog] && super.equals(obj)
}
Now we know what this is not about
but it’s pretty useless as is, so let’s rewind
Type classes
★ Enable ad-hoc polymorphism, aka overloading, without
inheritance
★ Type class is sort of an interface that defines some
behaviour and laws
★ Haskell introduced type classes to provide arithmetic and
equality operator overloading
★ In Scala type classes utilize traits, implicit context and
some syntactic sugar
Type classes - equality interface
trait Equality[T] {
def equals(a: T, b: T): Boolean
}
object Equality {
def equivalent[T](a: T, b: T)(implicit ev: Equality[T]): Boolean =
ev.equals(a, b)
def equivalent[T: Equality](a: T, b: T): Boolean =
implicitly[Equality[T]].equals(a, b)
}
Type classes - equality implementation
implicit val doubleEquality = new Equality[Double] {
override def equals(a: Double, b: Double): Boolean =
a == b
}
implicit val durationEquality = new Equality[Duration] {
override def equals(a: Duration, b: Duration): Boolean =
a.toNanos == b.toNanos
}
equivalent(1.3, 1.2)
//res0: false
equivalent(60.seconds, 1.minute)
//res1: true
Type classes - deserialization
trait Decoder[T] {
def decode(value: String): Either[DecodingError, T]
}
object Decoders {
implicit val jsonDecoder = new Decoder[Json] {
override def decode(value: String): Either[DecodingError, Json] =
JsonParser.parse(value) }
implicit val xmlDecoder = new Decoder[Xml] {
override def decode(value: String): Either[DecodingError, Xml] =
XmlReader.readInput(new ByteInputStream(value.getBytes())) }
}
Type classes - request handler
class RequestHandler[D: Decoder] {
def handleRequest(request: Request): Response = {
implicitly[Decoder[D]].decode(request.body) match {
case Left(error) => Response(400, error.message)
case Right(body) => Response(200, body)
}
}
}
import Decoders.jsonDecoder
val jsonRequestHandler = new RequestHandler[Json]
Functional programming
★ Paradigm that concentrates on computing results rather
than performing actions
★ Is essentially referential transparency - expression always
evaluates to the same result in any context
★ Pure functions are deterministic and do not cause any
observable side effects
★ Relies on immutability which means that state cannot
change after it’s created
Functional programming - pure functions
def add(a: Int, b: Int): Int = a + b
def sum(list: List[Int]): Int = list match {
case Nil => 0
case head :: tail => head + sum(tail)
}
List(1, 2, 3, 4, 5).map(n => n + 1)
Functional programming - referential transparency
ab + cd == add(a, b) + add(c, d)
val file = File("/dev/random")
val data = file.readBytes(10)
data ++ data != file.readBytes(10) ++ file.readBytes(10)
val atom = new AtomicInteger()
val value = atom.incrementAndGet()
value + value != atom.incrementAndGet() + atom.incrementAndGet()
Not RT
Not RT
Functional programming - composing
def subtract(a: Int, b: Int): Int = a - b
def multiply(v: Int, n: Int): Int = v * n
val subtractOne = (a: Int) => subtract(a, 1)
val multiplyFiveTimes = (v: Int) => multiply(v, 5)
List(1, 2, 3, 4, 5)
.map(subtractOne andThen multiplyFiveTimes)
.filter(_ % 2 == 0)
//res0: List(0, 10, 20)
Generic
★ Generalize code with type system assistance, for example
parametric types, generics or templates
★ Higher kinded types are repetition of polymorphism with
types, abstracting types that abstract types
★ Apply the principle of least power to pick the least
powerful solution capable of solving your problem
★ Code that is easy to change and interpret differently in the
future
Generic - first-order
trait List[T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
trait Monoid[A] {
def combine(x: A, y: A): A
def empty: A
}
Generic - composition
implicit val integerAddition: Monoid[Int] = new Monoid[Int] {
override def combine(x: Int, y: Int): Int = x + y
override def empty: Int = 0
}
def combineAll[A: Monoid](values: List[A]): A =
values.foldLeft(implicitly[Monoid[A]].empty)
(implicitly[Monoid[A]].combine)
Generic - higher order
trait Monad[F[_]] {
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def pure[A](x: A): F[A]
}
def map[A, B, F[_]: Monad](fa: F[A])(f: A => B): F[B] =
implicitly[Monad[F]].flatMap(fa)
(a => implicitly[Monad[F]].pure(f(a)))
Generic - higher order implementation
implicit val listMonad = new Monad[List] {
def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f)
def pure[A](x: A): List[A] = List(x)
}
implicit val streamMonad = new Monad[Stream] {
def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] =
fa.flatMap(f)
def pure[A](x: A): Stream[A] = Stream(x)
}
map(List(1, 2, 3, 4, 5))(_ + 1)
//res0: List(2, 3, 4, 5 ,6)
map(Stream(1, 2, 3, 4, 5))(_ + 1)
//res1: Stream(2, ?)
Tagless Final
★ Built on top of the previous patterns to create generic type
assisted domain specific languages
★ “Tagless” as there is no need for runtime tag checking to
perform type and error checking
★ “Final” as the interpretation is done in the target monad,
not deferred
★ Programs are expressions that are formed from operations
which are just functions
Tagless final - algebra and business logic
trait ContactRepository[F[_]] {
def get(name: String): F[Option[Contact]]
def update(contact: Contact): F[Contact]
}
class AddressBook[F[_]: Monad](cr: ContactRepository[F]) {
def updateAddress(n: String, addr: Address): F[Either[Error, Contact]] = {
cr.get(n) flatMap {
case None => Monad[F].pure(Left(NotFound(s"$n not found")))
case Some(c) => cr.update(c.copy(address = addr)).map(Right(_))
}
}
}
Tagless final - interpreters
class FutureInterpreter(db: Database) extends ContactRepository[Future] {
override def getContact(name: String): Future[Option[Contact]] =
db.findContact(name)
override def updateContact(contact: Contact): Future[Contact] =
db.updateContact(contact)
}
class IOInterpreter(dobbly: Dobbly) extends ContactRepository[IO] {
override def getContact(name: String): IO[Option[Contact]] =
dobbly.query(d"find contact name = $name")
override def updateContact(contact: Contact): IO[Contact] =
dobbly.query(d"update contact = $contact")
}
Tagless final - usage
val result: Future[Either[Error, Contact]] =
new AddressBook(new FutureInterpreter(db))
.updateContactAddress("John Doe", address)
val io: IO[Either[Error, Contact]] =
new AddressBook(new IOInterpreter(dobbly))
.updateContactAddress("John Doe", address)
val result: Future[Either[Error, Contact]] = io.transactionally().run()
All clear?
Scala is the gateway drug to Haskell

More Related Content

What's hot (20)

PDF
Fp in scala with adts part 2
Hang Zhao
 
PDF
Principled Error Handling with FP
Luka Jacobowitz
 
PDF
Fp in scala part 1
Hang Zhao
 
ODP
Beginning Scala Svcc 2009
David Pollak
 
PDF
Monoids, monoids, monoids
Luka Jacobowitz
 
PDF
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz
 
PDF
Scalaz 8: A Whole New Game
John De Goes
 
PDF
Oh, All the things you'll traverse
Luka Jacobowitz
 
PDF
Monoids, Monoids, Monoids - ScalaLove 2020
Luka Jacobowitz
 
PDF
Atomically { Delete Your Actors }
John De Goes
 
PPT
JBUG 11 - Scala For Java Programmers
Tikal Knowledge
 
PPTX
Introduction to Monads in Scala (1)
stasimus
 
PPT
Struct examples
mondalakash2012
 
PDF
First-Class Patterns
John De Goes
 
PDF
Why The Free Monad isn't Free
Kelley Robinson
 
PDF
Hammurabi
Mario Fusco
 
ODP
Functions In Scala
Knoldus Inc.
 
PDF
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
John De Goes
 
PPTX
The Essence of the Iterator Pattern
Eric Torreborre
 
PDF
Scala by Luc Duponcheel
Stephan Janssen
 
Fp in scala with adts part 2
Hang Zhao
 
Principled Error Handling with FP
Luka Jacobowitz
 
Fp in scala part 1
Hang Zhao
 
Beginning Scala Svcc 2009
David Pollak
 
Monoids, monoids, monoids
Luka Jacobowitz
 
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz
 
Scalaz 8: A Whole New Game
John De Goes
 
Oh, All the things you'll traverse
Luka Jacobowitz
 
Monoids, Monoids, Monoids - ScalaLove 2020
Luka Jacobowitz
 
Atomically { Delete Your Actors }
John De Goes
 
JBUG 11 - Scala For Java Programmers
Tikal Knowledge
 
Introduction to Monads in Scala (1)
stasimus
 
Struct examples
mondalakash2012
 
First-Class Patterns
John De Goes
 
Why The Free Monad isn't Free
Kelley Robinson
 
Hammurabi
Mario Fusco
 
Functions In Scala
Knoldus Inc.
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
John De Goes
 
The Essence of the Iterator Pattern
Eric Torreborre
 
Scala by Luc Duponcheel
Stephan Janssen
 

Similar to Generic Functional Programming with Type Classes (20)

PDF
Fp in scala part 2
Hang Zhao
 
PDF
Power of functions in a typed world
Debasish Ghosh
 
PDF
Scalapeno18 - Thinking Less with Scala
Daniel Sebban
 
PPTX
Scala - where objects and functions meet
Mario Fusco
 
PDF
Lecture 5: Functional Programming
Eelco Visser
 
PDF
T3chFest 2016 - The polyglot programmer
David Muñoz Díaz
 
PDF
Python idiomatico
PyCon Italia
 
PDF
Pydiomatic
rik0
 
PDF
3 kotlin vs. java- what kotlin has that java does not
Sergey Bandysik
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PPTX
Scala best practices
Alexander Zaidel
 
PPTX
R language introduction
Shashwat Shriparv
 
PPT
SDC - Einführung in Scala
Christian Baranowski
 
PPTX
R You Ready? An I/O Psychologist's Guide to R and Rstudio
sijan492614
 
PDF
Scala Functional Patterns
league
 
DOCX
ggtimeseries-->ggplot2 extensions
Dr. Volkan OBAN
 
PDF
Coding in Style
scalaconfjp
 
Fp in scala part 2
Hang Zhao
 
Power of functions in a typed world
Debasish Ghosh
 
Scalapeno18 - Thinking Less with Scala
Daniel Sebban
 
Scala - where objects and functions meet
Mario Fusco
 
Lecture 5: Functional Programming
Eelco Visser
 
T3chFest 2016 - The polyglot programmer
David Muñoz Díaz
 
Python idiomatico
PyCon Italia
 
Pydiomatic
rik0
 
3 kotlin vs. java- what kotlin has that java does not
Sergey Bandysik
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
Scala best practices
Alexander Zaidel
 
R language introduction
Shashwat Shriparv
 
SDC - Einführung in Scala
Christian Baranowski
 
R You Ready? An I/O Psychologist's Guide to R and Rstudio
sijan492614
 
Scala Functional Patterns
league
 
ggtimeseries-->ggplot2 extensions
Dr. Volkan OBAN
 
Coding in Style
scalaconfjp
 
Ad

More from Tapio Rautonen (8)

PDF
Deep dive into AWS CDK custom resources by Tapio Rautonen
Tapio Rautonen
 
PDF
The Public Cloud is a Lie
Tapio Rautonen
 
PDF
Making sense out of your big data
Tapio Rautonen
 
PDF
Adopting the Cloud
Tapio Rautonen
 
PDF
M.O.S.K.A. - Koulun penkiltä pelastamaan Suomea
Tapio Rautonen
 
PDF
Feedback loops - the second way towards the world of DevOps
Tapio Rautonen
 
PDF
Software Architecture for Cloud Infrastructure
Tapio Rautonen
 
PDF
Introduction to PaaS and Heroku
Tapio Rautonen
 
Deep dive into AWS CDK custom resources by Tapio Rautonen
Tapio Rautonen
 
The Public Cloud is a Lie
Tapio Rautonen
 
Making sense out of your big data
Tapio Rautonen
 
Adopting the Cloud
Tapio Rautonen
 
M.O.S.K.A. - Koulun penkiltä pelastamaan Suomea
Tapio Rautonen
 
Feedback loops - the second way towards the world of DevOps
Tapio Rautonen
 
Software Architecture for Cloud Infrastructure
Tapio Rautonen
 
Introduction to PaaS and Heroku
Tapio Rautonen
 
Ad

Recently uploaded (20)

PPTX
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
PPTX
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
PDF
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
PDF
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
PPTX
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
PDF
Executive Business Intelligence Dashboards
vandeslie24
 
PPTX
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
PPTX
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
PPTX
An Introduction to ZAP by Checkmarx - Official Version
Simon Bennetts
 
DOCX
Import Data Form Excel to Tally Services
Tally xperts
 
PDF
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
PDF
Streamline Contractor Lifecycle- TECH EHS Solution
TECH EHS Solution
 
PDF
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
PPTX
Human Resources Information System (HRIS)
Amity University, Patna
 
PPTX
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
PPT
MergeSortfbsjbjsfk sdfik k
RafishaikIT02044
 
PDF
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
PDF
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
PPTX
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
PPTX
MiniTool Power Data Recovery Full Crack Latest 2025
muhammadgurbazkhan
 
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
Executive Business Intelligence Dashboards
vandeslie24
 
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
An Introduction to ZAP by Checkmarx - Official Version
Simon Bennetts
 
Import Data Form Excel to Tally Services
Tally xperts
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
Streamline Contractor Lifecycle- TECH EHS Solution
TECH EHS Solution
 
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
Human Resources Information System (HRIS)
Amity University, Patna
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
MergeSortfbsjbjsfk sdfik k
RafishaikIT02044
 
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
MiniTool Power Data Recovery Full Crack Latest 2025
muhammadgurbazkhan
 

Generic Functional Programming with Type Classes

  • 1. Generic Functional Programming with Type Classes Tapio Rautonen
  • 2. Tapio Rautonen @trautonen De facto visibility optimization technology for video game industry and world leading 3D optimization and delivery platform Bending Scala and other technologies to provide the most immersive 3D experience from the cloud
  • 3. The following presentation shows some raw Scala code and might even discover the M-word. In case of dizzy vision or fuzzy sounds in your head, cover your eyes and ears and run. The presenter is not responsible if the audience turns functional programmers and introduce Haskell to their next project.
  • 4. Generic is not specific trait ContactRepository { def getContact(name: String): Future[Option[Contact]] def updateContact(contact: Contact): Future[Contact] } trait Calculus { def sum(x: Double, y: Double): Either[ArithmeticError, Double] def divide(x: Double, y: Double): Either[ArithmeticError, Double] def mean(xs: Iterable[Double]): Either[ArithmeticError, Double] }
  • 5. Functional programming is not object oriented class MissileSilo(control: MissileControl) { var missileCount: Int = 3 def launchMissile(): Unit = { missileCount = missileCount - 1 control.ignite() } }
  • 6. Type classes are not ad hoc inheritance abstract class Animal { var name: String = null def utter(): Unit override def equals(obj: Any): Boolean = obj.isInstanceOf[Animal] && obj.asInstanceOf[Animal].name == name } class Dog extends Animal { override def utter(): Unit = print("Wuf!") override def equals(obj: Any): Boolean = obj.isInstanceOf[Dog] && super.equals(obj) }
  • 7. Now we know what this is not about but it’s pretty useless as is, so let’s rewind
  • 8. Type classes ★ Enable ad-hoc polymorphism, aka overloading, without inheritance ★ Type class is sort of an interface that defines some behaviour and laws ★ Haskell introduced type classes to provide arithmetic and equality operator overloading ★ In Scala type classes utilize traits, implicit context and some syntactic sugar
  • 9. Type classes - equality interface trait Equality[T] { def equals(a: T, b: T): Boolean } object Equality { def equivalent[T](a: T, b: T)(implicit ev: Equality[T]): Boolean = ev.equals(a, b) def equivalent[T: Equality](a: T, b: T): Boolean = implicitly[Equality[T]].equals(a, b) }
  • 10. Type classes - equality implementation implicit val doubleEquality = new Equality[Double] { override def equals(a: Double, b: Double): Boolean = a == b } implicit val durationEquality = new Equality[Duration] { override def equals(a: Duration, b: Duration): Boolean = a.toNanos == b.toNanos } equivalent(1.3, 1.2) //res0: false equivalent(60.seconds, 1.minute) //res1: true
  • 11. Type classes - deserialization trait Decoder[T] { def decode(value: String): Either[DecodingError, T] } object Decoders { implicit val jsonDecoder = new Decoder[Json] { override def decode(value: String): Either[DecodingError, Json] = JsonParser.parse(value) } implicit val xmlDecoder = new Decoder[Xml] { override def decode(value: String): Either[DecodingError, Xml] = XmlReader.readInput(new ByteInputStream(value.getBytes())) } }
  • 12. Type classes - request handler class RequestHandler[D: Decoder] { def handleRequest(request: Request): Response = { implicitly[Decoder[D]].decode(request.body) match { case Left(error) => Response(400, error.message) case Right(body) => Response(200, body) } } } import Decoders.jsonDecoder val jsonRequestHandler = new RequestHandler[Json]
  • 13. Functional programming ★ Paradigm that concentrates on computing results rather than performing actions ★ Is essentially referential transparency - expression always evaluates to the same result in any context ★ Pure functions are deterministic and do not cause any observable side effects ★ Relies on immutability which means that state cannot change after it’s created
  • 14. Functional programming - pure functions def add(a: Int, b: Int): Int = a + b def sum(list: List[Int]): Int = list match { case Nil => 0 case head :: tail => head + sum(tail) } List(1, 2, 3, 4, 5).map(n => n + 1)
  • 15. Functional programming - referential transparency ab + cd == add(a, b) + add(c, d) val file = File("/dev/random") val data = file.readBytes(10) data ++ data != file.readBytes(10) ++ file.readBytes(10) val atom = new AtomicInteger() val value = atom.incrementAndGet() value + value != atom.incrementAndGet() + atom.incrementAndGet() Not RT Not RT
  • 16. Functional programming - composing def subtract(a: Int, b: Int): Int = a - b def multiply(v: Int, n: Int): Int = v * n val subtractOne = (a: Int) => subtract(a, 1) val multiplyFiveTimes = (v: Int) => multiply(v, 5) List(1, 2, 3, 4, 5) .map(subtractOne andThen multiplyFiveTimes) .filter(_ % 2 == 0) //res0: List(0, 10, 20)
  • 17. Generic ★ Generalize code with type system assistance, for example parametric types, generics or templates ★ Higher kinded types are repetition of polymorphism with types, abstracting types that abstract types ★ Apply the principle of least power to pick the least powerful solution capable of solving your problem ★ Code that is easy to change and interpret differently in the future
  • 18. Generic - first-order trait List[T] { def isEmpty: Boolean def head: T def tail: List[T] } trait Monoid[A] { def combine(x: A, y: A): A def empty: A }
  • 19. Generic - composition implicit val integerAddition: Monoid[Int] = new Monoid[Int] { override def combine(x: Int, y: Int): Int = x + y override def empty: Int = 0 } def combineAll[A: Monoid](values: List[A]): A = values.foldLeft(implicitly[Monoid[A]].empty) (implicitly[Monoid[A]].combine)
  • 20. Generic - higher order trait Monad[F[_]] { def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def pure[A](x: A): F[A] } def map[A, B, F[_]: Monad](fa: F[A])(f: A => B): F[B] = implicitly[Monad[F]].flatMap(fa) (a => implicitly[Monad[F]].pure(f(a)))
  • 21. Generic - higher order implementation implicit val listMonad = new Monad[List] { def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f) def pure[A](x: A): List[A] = List(x) } implicit val streamMonad = new Monad[Stream] { def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] = fa.flatMap(f) def pure[A](x: A): Stream[A] = Stream(x) } map(List(1, 2, 3, 4, 5))(_ + 1) //res0: List(2, 3, 4, 5 ,6) map(Stream(1, 2, 3, 4, 5))(_ + 1) //res1: Stream(2, ?)
  • 22. Tagless Final ★ Built on top of the previous patterns to create generic type assisted domain specific languages ★ “Tagless” as there is no need for runtime tag checking to perform type and error checking ★ “Final” as the interpretation is done in the target monad, not deferred ★ Programs are expressions that are formed from operations which are just functions
  • 23. Tagless final - algebra and business logic trait ContactRepository[F[_]] { def get(name: String): F[Option[Contact]] def update(contact: Contact): F[Contact] } class AddressBook[F[_]: Monad](cr: ContactRepository[F]) { def updateAddress(n: String, addr: Address): F[Either[Error, Contact]] = { cr.get(n) flatMap { case None => Monad[F].pure(Left(NotFound(s"$n not found"))) case Some(c) => cr.update(c.copy(address = addr)).map(Right(_)) } } }
  • 24. Tagless final - interpreters class FutureInterpreter(db: Database) extends ContactRepository[Future] { override def getContact(name: String): Future[Option[Contact]] = db.findContact(name) override def updateContact(contact: Contact): Future[Contact] = db.updateContact(contact) } class IOInterpreter(dobbly: Dobbly) extends ContactRepository[IO] { override def getContact(name: String): IO[Option[Contact]] = dobbly.query(d"find contact name = $name") override def updateContact(contact: Contact): IO[Contact] = dobbly.query(d"update contact = $contact") }
  • 25. Tagless final - usage val result: Future[Either[Error, Contact]] = new AddressBook(new FutureInterpreter(db)) .updateContactAddress("John Doe", address) val io: IO[Either[Error, Contact]] = new AddressBook(new IOInterpreter(dobbly)) .updateContactAddress("John Doe", address) val result: Future[Either[Error, Contact]] = io.transactionally().run()
  • 26. All clear? Scala is the gateway drug to Haskell