SlideShare a Scribd company logo
Big Step To Functional
Programming*
* - beginners level
Alex Zvolinskiy
Senior Scala Developer at VidIQ
Blog: www.Fruzenshtein.com
Twitter: @Fruzenshtein
LinkedIn, Facebook, GitHub
Motivation
Conner Baker @connerbaker
Mauna Kea Observatory, Waimea, United States
Real Life Example
User Service
addUser
getUser
Offer Service
addOffer
getOffer
deleteOffer
Basic Types
type UserId = String
type Name = String
type OfferId = Int
type Message = String
final case class User(id: UserId, name: Name)
final case class Offer(id: OfferId, userId: UserId, message: Message, active: Boolean)
type UserStorage = collection.mutable.Map[UserId, User]
type OfferStorage = collection.mutable.Map[OfferId, Offer]
Standard Solution
trait UserDAO {
def addUser(user: User): Unit
def getUser(id: UserId): Option[User]
}
class UserDaoImpl(val storage: UserStorage) extends UserDAO {
def addUser(user: User): Unit = storage(user.id) = user
def getUser(id: UserId): Option[User] = storage.get(id)
}
Standard Solution
trait OfferDAO {
def addOffer(offer: Offer): Unit
def getOffer(offerId: OfferId): Option[Offer]
def deleteOffer(offerId: OfferId): Unit
}
class OfferDaoImpl(val storage: OfferStorage) extends OfferDAO {
override def addOffer(offer: Offer): Unit =
storage(offer.id) = offer
override def getOffer(offerId: OfferId): Option[Offer] =
storage.get(offerId)
override def deleteOffer(offerId: OfferId): Unit =
storage -= offerId
}
Standard Solution
val userDB = mutable.Map.empty[UserId, User]
val userDao = new UserDaoImpl(userDB)
userDao.addUser(User("uid01", "Alex"))
val user = userDao.getUser("uid01")
println(user)
for {
_ <- userDao.addUser(User("uid01", "Alex"))
user <- userDao.getUser("uid01")
} yield {
println(user)
}
Error: value flatMap is not a member of Unit
_ <- userDao.addUser(User("uid01", "Alex"))
Caution: Cats
point[M[_], A](a: A): M[A]
flatMap[M[_], A, B](fa: M[A])(f: A => M[B]): M[B]
“Another” Solution
sealed trait UserOps[A]
final case class Add(user: User) extends UserOps[Unit]
final case class Get(id: UserId) extends UserOps[Option[User]]
import cats.free.Free
type UserStorage[A] = Free[UserOps, A]
import cats.free.Free.liftF
def add(user: User): UserStorage[Unit] = liftF[UserOps, Unit](Add(user))
def get(id: UserId): UserStorage[Option[User]] = liftF[UserOps, Option[User]](Get(id))
import cats.{Id, ~>}
import scala.collection.mutable
def compiler(): UserOps ~> Id = new (UserOps ~> Id) {
val userStorage = mutable.Map.empty[UserId, User]
override def apply[A](fa: UserOps[A]): Id[A] = fa match {
case Add(user) => userStorage(user.id) = user
case Get(id) => userStorage.get(id)
}
}
“Another” Solution
import cats.{Id, ~>}
import scala.collection.mutable
def compiler(): UserOps ~> Id = new (UserOps ~> Id) {
val userStorage = mutable.Map.empty[UserId, User]
override def apply[A](fa: UserOps[A]): Id[A] = fa match {
case Add(user) => userStorage(user.id) = user
case Get(id) => userStorage.get(id)
}
}
val computations =
for {
_ <- add(User("uid01", "Alex"))
user <- get("uid01")
} yield {
println(user) //Some(User(uid01,Alex))
}
computations.foldMap(compiler)
Steps
1. Define ADT
2. Create a Free[_] for the ADT
3. Write a compiler
4. Compose operations in a program
5. Run the program
What about async?
trait UserDAO {
implicit val ec: ExecutionContext
val storage: UserStorage
def addUser(user: User): Future[Unit]
def getUser(id: UserId): Future[Option[User]]
}
class UserDaoImpl(val storage: UserStorage)
(implicit val ec: ExecutionContext) extends UserDAO {
def addUser(user: User): Future[Unit] = Future(storage(user.id) = user)
def getUser(id: UserId): Future[Option[User]] = Future(storage.get(id))
}
trait OfferDAO {
...
}
implicit val ec = ExecutionContext.global
val userDB = mutable.Map.empty[UserId, User]
val userDao = new UserDaoImpl(userDB)
val offerDB = mutable.Map.empty[OfferId, Offer]
val offerDao = new OfferDaoImpl(offerDB)
val result = for {
_ <- userDao.addUser(User("uid01", "Alex"))
_ <- offerDao.addOffer(Offer(1, "uid01", "10% discount", false))
offerAdded <- offerDao.getOffer(1)
_ <- offerDao.deleteOffer(1)
offerDeleted <- offerDao.getOffer(1)
} yield {
println(offerAdded) //Some(Offer(1,uid01,10% discount,false))
println(offerDeleted) //None
}
Await.result(result, 1.second)
“Another” Async Solution
sealed trait UserOps[A]
final case class Add(user: User) extends UserOps[Unit]
final case class Get(id: UserId) extends UserOps[Option[User]]
import cats.free.Free
type UserStorage[A] = Free[UserOps, A]
import cats.free.Free.liftF
def add(user: User): UserStorage[Unit] = liftF[UserOps, Unit](Add(user))
def get(id: UserId): UserStorage[Option[User]] = liftF[UserOps, Option[User]](Get(id))
Different part of async Solution
import cats.~>
import cats.implicits._
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
def compiler: UserOps ~> Future = new (UserOps ~> Future) {
val userStorage = mutable.Map.empty[UserId, User]
override def apply[A](fa: UserOps[A]): Future[A] = fa match {
case Add(user) => Future(userStorage(user.id) = user)
case Get(id) => Future(userStorage.get(id))
}
}
Async Program Run
val computations =
for {
_ <- add(User("uid01", "Alex"))
user <- get("uid01")
} yield {
println(user) //Some(User(uid01,Alex))
}
import scala.concurrent.duration._
Await.result(computations.foldMap(compiler), 1.second)
What about composition?
sealed trait UserOps[A]
final case class AddUser(user: User) extends UserOps[Unit]
final case class GetUser(id: UserId) extends UserOps[Option[User]]
sealed trait OfferOps[A]
final case class AddOffer(offer: Offer) extends OfferOps[Unit]
final case class GetOffer(id: OfferId) extends OfferOps[Option[Offer]]
final case class DeleteOffer(id: OfferId) extends OfferOps[Unit]
type UserOfferApp[A] = EitherK[UserOps, OfferOps, A]
Free[_] types for ADTs
class UserOperations[F[_]](implicit I: InjectK[UserOps, F]) {
def addUser(user: User): Free[F, Unit] = Free.inject[UserOps, F](AddUser(user))
def getUser(id: UserId): Free[F, Option[User]] = Free.inject[UserOps, F](GetUser(id))
}
object UserOperations {
implicit def userOperations[F[_]](implicit I: InjectK[UserOps, F]): UserOperations[F] =
new UserOperations[F]
}
class OfferOperations[F[_]](implicit I: InjectK[OfferOps, F]) {
def addOffer(offer: Offer): Free[F, Unit] = Free.inject[OfferOps, F](AddOffer(offer))
def getOffer(id: OfferId): Free[F, Option[Offer]] = Free.inject[OfferOps, F](GetOffer(id))
def deleteOffer(id: OfferId): Free[F, Unit] = Free.inject[OfferOps, F](DeleteOffer(id))
}
object OfferOperations {
implicit def offerOperations[F[_]](implicit I: InjectK[OfferOps, F]): OfferOperations[F] =
new OfferOperations[F]
}
Interpreters
object UserOpsInterpreter extends (UserOps ~> Id) {
val userStorage = mutable.Map.empty[UserId, User]
override def apply[A](fa: UserOps[A]) = fa match {
case AddUser(user) => userStorage(user.id) = user
case GetUser(id) => userStorage.get(id)
}
}
object OfferOpsInterpreter extends (OfferOps ~> Id) {
val offerStorage = mutable.Map.empty[OfferId, Offer]
override def apply[A](fa: OfferOps[A]) = fa match {
case AddOffer(offer) => offerStorage(offer.id) = offer
case GetOffer(id) => offerStorage.get(id)
case DeleteOffer(id) => offerStorage -= id
()
}
}
val mainInterpreter: UserOfferApp ~> Id = UserOpsInterpreter or OfferOpsInterpreter
ADTs into program
def program(implicit UO: UserOperations[UserOfferApp],
OO: OfferOperations[UserOfferApp]): Free[UserOfferApp, Unit] = {
import UO._, OO._
for {
_ <- addUser(User("uid01", "Alex"))
_ <- addOffer(Offer(1, "uid01", "10% discount", true))
addedOffer <- getOffer(1)
_ <- deleteOffer(1)
deletedOffer <- getOffer(1)
} yield {
println(addedOffer) //Some(Offer(1,uid01,10% discount,true))
println(deletedOffer) //None
}
}
Run a program
import UserOperations._, OfferOperations._
program.foldMap(mainInterpreter)
Thanks
https://github.com/Fruzenshtein/ScalaUA-2019

More Related Content

What's hot (20)

PDF
The Ring programming language version 1.5.2 book - Part 37 of 181
Mahmoud Samir Fayed
 
PDF
The Ring programming language version 1.6 book - Part 40 of 189
Mahmoud Samir Fayed
 
PDF
Deep-dive into Django #1
Avik Das
 
PPTX
Web Development Course - JQuery by RSOLUTIONS
RSolutions
 
PDF
The Ring programming language version 1.2 book - Part 32 of 84
Mahmoud Samir Fayed
 
PPTX
Object Oriented JavaScript
Julie Iskander
 
PDF
The Ring programming language version 1.5.2 book - Part 29 of 181
Mahmoud Samir Fayed
 
PDF
The Ring programming language version 1.5.1 book - Part 43 of 180
Mahmoud Samir Fayed
 
PDF
The Ring programming language version 1.8 book - Part 80 of 202
Mahmoud Samir Fayed
 
PDF
Types and Immutability: why you should care
Jean Carlo Emer
 
PDF
The Ring programming language version 1.4 book - Part 8 of 30
Mahmoud Samir Fayed
 
PDF
Scala DSLの作り方
Tomoharu ASAMI
 
PPTX
Java script
Shagufta shaheen
 
PPTX
Ggplot2 v3
Josh Doyle
 
PDF
The Ring programming language version 1.9 book - Part 46 of 210
Mahmoud Samir Fayed
 
PDF
The Ring programming language version 1.6 book - Part 46 of 189
Mahmoud Samir Fayed
 
PDF
The Ring programming language version 1.5 book - Part 3 of 31
Mahmoud Samir Fayed
 
ODP
Dependency injection in Scala
Knoldus Inc.
 
PDF
Structuring React.js Components
Bartek Witczak
 
PDF
4Developers 2018: Structuring React components (Bartłomiej Witczak)
PROIDEA
 
The Ring programming language version 1.5.2 book - Part 37 of 181
Mahmoud Samir Fayed
 
The Ring programming language version 1.6 book - Part 40 of 189
Mahmoud Samir Fayed
 
Deep-dive into Django #1
Avik Das
 
Web Development Course - JQuery by RSOLUTIONS
RSolutions
 
The Ring programming language version 1.2 book - Part 32 of 84
Mahmoud Samir Fayed
 
Object Oriented JavaScript
Julie Iskander
 
The Ring programming language version 1.5.2 book - Part 29 of 181
Mahmoud Samir Fayed
 
The Ring programming language version 1.5.1 book - Part 43 of 180
Mahmoud Samir Fayed
 
The Ring programming language version 1.8 book - Part 80 of 202
Mahmoud Samir Fayed
 
Types and Immutability: why you should care
Jean Carlo Emer
 
The Ring programming language version 1.4 book - Part 8 of 30
Mahmoud Samir Fayed
 
Scala DSLの作り方
Tomoharu ASAMI
 
Java script
Shagufta shaheen
 
Ggplot2 v3
Josh Doyle
 
The Ring programming language version 1.9 book - Part 46 of 210
Mahmoud Samir Fayed
 
The Ring programming language version 1.6 book - Part 46 of 189
Mahmoud Samir Fayed
 
The Ring programming language version 1.5 book - Part 3 of 31
Mahmoud Samir Fayed
 
Dependency injection in Scala
Knoldus Inc.
 
Structuring React.js Components
Bartek Witczak
 
4Developers 2018: Structuring React components (Bartłomiej Witczak)
PROIDEA
 

Similar to Scala UA: Big Step To Functional Programming (20)

PDF
Event Sourcing and Functional Programming
GlobalLogic Ukraine
 
PDF
Functional Programming & Event Sourcing - a pair made in heaven
Pawel Szulc
 
PPTX
Taxonomy of Scala
shinolajla
 
PDF
Async Microservices with Twitter's Finagle
Vladimir Kostyukov
 
PDF
Demystifying functional programming with Scala
Denis
 
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
 
PDF
Option, Either, Try and what to do with corner cases when they arise
Michal Bigos
 
PDF
Introduction to scala
Michel Perez
 
PPT
Functional programming with_scala
Raymond Tay
 
PDF
Scala taxonomy
Radim Pavlicek
 
PDF
ScalaFlavor4J
Kazuhiro Sera
 
PDF
Functional programming in Scala
Damian Jureczko
 
PPTX
Concurrent Application Development using Scala
Siarhiej Siemianchuk
 
PDF
From OOP To FP Through A Practical Case
Cristina Delgado Rodríguez
 
PDF
Functional Programming from OO perspective (Sayeret Lambda lecture)
Ittay Dror
 
PPTX
Analyzing Functional Programs
Dave Cleaver
 
PDF
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
Event Sourcing and Functional Programming
GlobalLogic Ukraine
 
Functional Programming & Event Sourcing - a pair made in heaven
Pawel Szulc
 
Taxonomy of Scala
shinolajla
 
Async Microservices with Twitter's Finagle
Vladimir Kostyukov
 
Demystifying functional programming with Scala
Denis
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
Option, Either, Try and what to do with corner cases when they arise
Michal Bigos
 
Introduction to scala
Michel Perez
 
Functional programming with_scala
Raymond Tay
 
Scala taxonomy
Radim Pavlicek
 
ScalaFlavor4J
Kazuhiro Sera
 
Functional programming in Scala
Damian Jureczko
 
Concurrent Application Development using Scala
Siarhiej Siemianchuk
 
From OOP To FP Through A Practical Case
Cristina Delgado Rodríguez
 
Functional Programming from OO perspective (Sayeret Lambda lecture)
Ittay Dror
 
Analyzing Functional Programs
Dave Cleaver
 
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
Ad

More from Alex Fruzenshtein (7)

PDF
Akka: Actor Design & Communication Technics
Alex Fruzenshtein
 
PDF
Spark as a distributed Scala
Alex Fruzenshtein
 
PDF
Spring IO for startups
Alex Fruzenshtein
 
PDF
Boost UI tests
Alex Fruzenshtein
 
PDF
N аргументов не идти в QA
Alex Fruzenshtein
 
PDF
XpDays - Automated testing of responsive design (GalenFramework)
Alex Fruzenshtein
 
PPTX
Test-case design
Alex Fruzenshtein
 
Akka: Actor Design & Communication Technics
Alex Fruzenshtein
 
Spark as a distributed Scala
Alex Fruzenshtein
 
Spring IO for startups
Alex Fruzenshtein
 
Boost UI tests
Alex Fruzenshtein
 
N аргументов не идти в QA
Alex Fruzenshtein
 
XpDays - Automated testing of responsive design (GalenFramework)
Alex Fruzenshtein
 
Test-case design
Alex Fruzenshtein
 
Ad

Recently uploaded (20)

PDF
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PDF
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 
PDF
Rethinking Security Operations - SOC Evolution Journey.pdf
Haris Chughtai
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
PDF
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PPTX
Top Managed Service Providers in Los Angeles
Captain IT
 
PDF
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
PDF
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
PDF
Impact of IEEE Computer Society in Advancing Emerging Technologies including ...
Hironori Washizaki
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PDF
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
PPTX
UiPath Academic Alliance Educator Panels: Session 2 - Business Analyst Content
DianaGray10
 
PDF
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
PDF
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
PDF
Empowering Cloud Providers with Apache CloudStack and Stackbill
ShapeBlue
 
PDF
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
PDF
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 
Rethinking Security Operations - SOC Evolution Journey.pdf
Haris Chughtai
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Top Managed Service Providers in Los Angeles
Captain IT
 
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
Impact of IEEE Computer Society in Advancing Emerging Technologies including ...
Hironori Washizaki
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
UiPath Academic Alliance Educator Panels: Session 2 - Business Analyst Content
DianaGray10
 
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
Empowering Cloud Providers with Apache CloudStack and Stackbill
ShapeBlue
 
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 

Scala UA: Big Step To Functional Programming

  • 1. Big Step To Functional Programming* * - beginners level
  • 2. Alex Zvolinskiy Senior Scala Developer at VidIQ Blog: www.Fruzenshtein.com Twitter: @Fruzenshtein LinkedIn, Facebook, GitHub
  • 3. Motivation Conner Baker @connerbaker Mauna Kea Observatory, Waimea, United States
  • 4. Real Life Example User Service addUser getUser Offer Service addOffer getOffer deleteOffer
  • 5. Basic Types type UserId = String type Name = String type OfferId = Int type Message = String final case class User(id: UserId, name: Name) final case class Offer(id: OfferId, userId: UserId, message: Message, active: Boolean) type UserStorage = collection.mutable.Map[UserId, User] type OfferStorage = collection.mutable.Map[OfferId, Offer]
  • 6. Standard Solution trait UserDAO { def addUser(user: User): Unit def getUser(id: UserId): Option[User] } class UserDaoImpl(val storage: UserStorage) extends UserDAO { def addUser(user: User): Unit = storage(user.id) = user def getUser(id: UserId): Option[User] = storage.get(id) }
  • 7. Standard Solution trait OfferDAO { def addOffer(offer: Offer): Unit def getOffer(offerId: OfferId): Option[Offer] def deleteOffer(offerId: OfferId): Unit } class OfferDaoImpl(val storage: OfferStorage) extends OfferDAO { override def addOffer(offer: Offer): Unit = storage(offer.id) = offer override def getOffer(offerId: OfferId): Option[Offer] = storage.get(offerId) override def deleteOffer(offerId: OfferId): Unit = storage -= offerId }
  • 8. Standard Solution val userDB = mutable.Map.empty[UserId, User] val userDao = new UserDaoImpl(userDB) userDao.addUser(User("uid01", "Alex")) val user = userDao.getUser("uid01") println(user) for { _ <- userDao.addUser(User("uid01", "Alex")) user <- userDao.getUser("uid01") } yield { println(user) } Error: value flatMap is not a member of Unit _ <- userDao.addUser(User("uid01", "Alex"))
  • 9. Caution: Cats point[M[_], A](a: A): M[A] flatMap[M[_], A, B](fa: M[A])(f: A => M[B]): M[B]
  • 10. “Another” Solution sealed trait UserOps[A] final case class Add(user: User) extends UserOps[Unit] final case class Get(id: UserId) extends UserOps[Option[User]] import cats.free.Free type UserStorage[A] = Free[UserOps, A] import cats.free.Free.liftF def add(user: User): UserStorage[Unit] = liftF[UserOps, Unit](Add(user)) def get(id: UserId): UserStorage[Option[User]] = liftF[UserOps, Option[User]](Get(id)) import cats.{Id, ~>} import scala.collection.mutable def compiler(): UserOps ~> Id = new (UserOps ~> Id) { val userStorage = mutable.Map.empty[UserId, User] override def apply[A](fa: UserOps[A]): Id[A] = fa match { case Add(user) => userStorage(user.id) = user case Get(id) => userStorage.get(id) } }
  • 11. “Another” Solution import cats.{Id, ~>} import scala.collection.mutable def compiler(): UserOps ~> Id = new (UserOps ~> Id) { val userStorage = mutable.Map.empty[UserId, User] override def apply[A](fa: UserOps[A]): Id[A] = fa match { case Add(user) => userStorage(user.id) = user case Get(id) => userStorage.get(id) } } val computations = for { _ <- add(User("uid01", "Alex")) user <- get("uid01") } yield { println(user) //Some(User(uid01,Alex)) } computations.foldMap(compiler)
  • 12. Steps 1. Define ADT 2. Create a Free[_] for the ADT 3. Write a compiler 4. Compose operations in a program 5. Run the program
  • 13. What about async? trait UserDAO { implicit val ec: ExecutionContext val storage: UserStorage def addUser(user: User): Future[Unit] def getUser(id: UserId): Future[Option[User]] } class UserDaoImpl(val storage: UserStorage) (implicit val ec: ExecutionContext) extends UserDAO { def addUser(user: User): Future[Unit] = Future(storage(user.id) = user) def getUser(id: UserId): Future[Option[User]] = Future(storage.get(id)) } trait OfferDAO { ... }
  • 14. implicit val ec = ExecutionContext.global val userDB = mutable.Map.empty[UserId, User] val userDao = new UserDaoImpl(userDB) val offerDB = mutable.Map.empty[OfferId, Offer] val offerDao = new OfferDaoImpl(offerDB) val result = for { _ <- userDao.addUser(User("uid01", "Alex")) _ <- offerDao.addOffer(Offer(1, "uid01", "10% discount", false)) offerAdded <- offerDao.getOffer(1) _ <- offerDao.deleteOffer(1) offerDeleted <- offerDao.getOffer(1) } yield { println(offerAdded) //Some(Offer(1,uid01,10% discount,false)) println(offerDeleted) //None } Await.result(result, 1.second)
  • 15. “Another” Async Solution sealed trait UserOps[A] final case class Add(user: User) extends UserOps[Unit] final case class Get(id: UserId) extends UserOps[Option[User]] import cats.free.Free type UserStorage[A] = Free[UserOps, A] import cats.free.Free.liftF def add(user: User): UserStorage[Unit] = liftF[UserOps, Unit](Add(user)) def get(id: UserId): UserStorage[Option[User]] = liftF[UserOps, Option[User]](Get(id))
  • 16. Different part of async Solution import cats.~> import cats.implicits._ import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global def compiler: UserOps ~> Future = new (UserOps ~> Future) { val userStorage = mutable.Map.empty[UserId, User] override def apply[A](fa: UserOps[A]): Future[A] = fa match { case Add(user) => Future(userStorage(user.id) = user) case Get(id) => Future(userStorage.get(id)) } }
  • 17. Async Program Run val computations = for { _ <- add(User("uid01", "Alex")) user <- get("uid01") } yield { println(user) //Some(User(uid01,Alex)) } import scala.concurrent.duration._ Await.result(computations.foldMap(compiler), 1.second)
  • 18. What about composition? sealed trait UserOps[A] final case class AddUser(user: User) extends UserOps[Unit] final case class GetUser(id: UserId) extends UserOps[Option[User]] sealed trait OfferOps[A] final case class AddOffer(offer: Offer) extends OfferOps[Unit] final case class GetOffer(id: OfferId) extends OfferOps[Option[Offer]] final case class DeleteOffer(id: OfferId) extends OfferOps[Unit] type UserOfferApp[A] = EitherK[UserOps, OfferOps, A]
  • 19. Free[_] types for ADTs class UserOperations[F[_]](implicit I: InjectK[UserOps, F]) { def addUser(user: User): Free[F, Unit] = Free.inject[UserOps, F](AddUser(user)) def getUser(id: UserId): Free[F, Option[User]] = Free.inject[UserOps, F](GetUser(id)) } object UserOperations { implicit def userOperations[F[_]](implicit I: InjectK[UserOps, F]): UserOperations[F] = new UserOperations[F] } class OfferOperations[F[_]](implicit I: InjectK[OfferOps, F]) { def addOffer(offer: Offer): Free[F, Unit] = Free.inject[OfferOps, F](AddOffer(offer)) def getOffer(id: OfferId): Free[F, Option[Offer]] = Free.inject[OfferOps, F](GetOffer(id)) def deleteOffer(id: OfferId): Free[F, Unit] = Free.inject[OfferOps, F](DeleteOffer(id)) } object OfferOperations { implicit def offerOperations[F[_]](implicit I: InjectK[OfferOps, F]): OfferOperations[F] = new OfferOperations[F] }
  • 20. Interpreters object UserOpsInterpreter extends (UserOps ~> Id) { val userStorage = mutable.Map.empty[UserId, User] override def apply[A](fa: UserOps[A]) = fa match { case AddUser(user) => userStorage(user.id) = user case GetUser(id) => userStorage.get(id) } } object OfferOpsInterpreter extends (OfferOps ~> Id) { val offerStorage = mutable.Map.empty[OfferId, Offer] override def apply[A](fa: OfferOps[A]) = fa match { case AddOffer(offer) => offerStorage(offer.id) = offer case GetOffer(id) => offerStorage.get(id) case DeleteOffer(id) => offerStorage -= id () } } val mainInterpreter: UserOfferApp ~> Id = UserOpsInterpreter or OfferOpsInterpreter
  • 21. ADTs into program def program(implicit UO: UserOperations[UserOfferApp], OO: OfferOperations[UserOfferApp]): Free[UserOfferApp, Unit] = { import UO._, OO._ for { _ <- addUser(User("uid01", "Alex")) _ <- addOffer(Offer(1, "uid01", "10% discount", true)) addedOffer <- getOffer(1) _ <- deleteOffer(1) deletedOffer <- getOffer(1) } yield { println(addedOffer) //Some(Offer(1,uid01,10% discount,true)) println(deletedOffer) //None } }
  • 22. Run a program import UserOperations._, OfferOperations._ program.foldMap(mainInterpreter)