SlideShare a Scribd company logo
1 of 63
Download to read offline
Boring Usecases
for Exciting Types
Itamar Ravid - FLIP '18
Itamar Ravid - @itrvd 1
About me
• Freelance software developer
• Doing functional programming, microservices, DevOps
• Co-organize the Underscore Scala meetup
• Hit me up if you want to work together!
Itamar Ravid - @itrvd 2
Agenda
• Why I wrote this talk
• Three data types and their usecases
Itamar Ravid - @itrvd 3
Programming with functions
Itamar Ravid - @itrvd 4
Itamar Ravid - @itrvd 5
How do we grow
as functional programmers
Itamar Ravid - @itrvd 6
Classic "draw an owl" problem
How do we go from this:
quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter ( >= p) xs
Itamar Ravid - @itrvd 7
Classic "draw an owl" problem
To this:
-- | Fokkinga's postpromorphism
postpro
:: Recursive t
=> (forall b. Base t b -> Base t b) -- natural transformation
-> (a -> Base t a) -- a (Base t)-coalgebra
-> a -- seed
-> t
postpro e g = a where a = embed . fmap (ana (e . project) . a) . g
Itamar Ravid - @itrvd 8
Examples
Itamar Ravid - @itrvd 9
Examples
We are sorely lacking!
Itamar Ravid - @itrvd 10
Actual agenda
Purely functional and type-safe:
• Time measurement and instrumentation
• State machines
• Resource allocation and release
• Let's go!
Itamar Ravid - @itrvd 11
Examples ahead assume:
import cats._, cats.data._, cats.implicits._
import cats.effect._
Itamar Ravid - @itrvd 12
Time measurement and instrumentation
Problem: I have a bunch of computations, and I want to measure
them.
def computeCv(data: Data): IO[Double] =
for {
sample <- computeSample(data)
mean <- computeMean(sample)
variance <- computeVariance(sample)
} yield mean / variance
Itamar Ravid - @itrvd 13
Time measurement and instrumentation
Non-solution: manually instrument.
def computeCv(data: Data): IO[Double] =
for {
startTime <- time()
sample <- computeSample(data)
sampleTime <- time()
mean <- computeMean(sample)
meanTime <- time()
variance <- computeVariance(sample)
varTime <- time()
} yield mean / variance
Itamar Ravid - @itrvd 14
Itamar Ravid - @itrvd 15
Time measurement and instrumentation
To start, we define a timed combinator:
def timed[A](fa: IO[A]): IO[(FiniteDuration, A)] =
for {
startTime <- time()
a <- fa
endTime <- time()
} yield ((endTime - startTime).millis, a)
fa is pure, so we can just pass it around.
Itamar Ravid - @itrvd 16
Time measurement and instrumentation
We can also name this computation:
def timed[A](fa: IO[A],
name: String): IO[((String, FiniteDuration), A)] =
for {
startTime <- time()
a <- fa
endTime <- time()
} yield (name -> (endTime - startTime).millis, a)
Itamar Ravid - @itrvd 17
Time measurement and instrumentation
We can now use our new timed function:
def computeCv(data: Data): IO[Double] =
for {
(sampleTime, sample) <- timed(computeSample(data), "sample")
(meanTime, mean) <- timed(computeMean(sample), "mean")
(varTime, variance) <- timed(computeVariance(sample), "var")
} yield mean / variance
Kinda tedious though!
Itamar Ravid - @itrvd 18
Time measurement and instrumentation
To save us from that bookeeping, comes WriterT:
case class WriterT[F[_], Log, Value](
run: F[(Log, Value)]
)
Itamar Ravid - @itrvd 19
Time measurement and instrumentation
To save us from that bookeeping, comes WriterT:
case class WriterT[F[_], Log, Value](
run: F[(Log, Value)]
)
If Log: Semigroup- we can merge logs.
Itamar Ravid - @itrvd 20
Time measurement and instrumentation
We modify timed to return a WriterT:
type Timing = Map[String, FiniteDuration]
def timed(name: String, fa: IO[A]): WriterT[IO, Timing, A] =
WriterT {
for {
startTime <- time()
a <- fa
endTime <- time()
} yield (Map(name -> (endTime - startTime).millis), a)
}
Itamar Ravid - @itrvd 21
Time measurement and instrumentation
And we can call this Timed[A] for nicety:
type Timed[A] = WriterT[IO, Map[String, FiniteDuration], A]
def timed(name: String, fa: IO[A]): Timed[A] =
WriterT {
for {
startTime <- time()
a <- fa
endTime <- time()
} yield (Map(name -> (endTime - startTime).millis), a)
}
Itamar Ravid - @itrvd 22
Time measurement and instrumentation
Our original program is only slightly changed:
def computeCv(data: Data): Timed[Double] =
for {
sample <- timed(computeSample(data), "sample")
mean <- timed(computeMean(sample), "mean")
variance <- timed(computeVariance(sample), "var")
} yield mean / variance
Itamar Ravid - @itrvd 23
Time measurement and instrumentation
To unpack the timing data, we can define another function:
def logTimings[A](timed: Timed[A]): IO[A] =
for {
(timing, a) <- timed.run
_ <- IO(println(timing))
} yield a
Itamar Ravid - @itrvd 24
Time measurement and instrumentation
And use it:
def computeCv(data: Data): IO[Double] =
logTimings {
for {
sample <- timed(computeSample(data), "sample")
mean <- timed(computeMean(sample), "mean")
variance <- timed(computeVariance(sample), "var")
} yield mean / variance
}
Itamar Ravid - @itrvd 25
Summary
• You can add infix syntax using implicit class for nicety:
computeSample(data).timed("sample").logTimings,
etc.
• WriterT is a Monad and an Applicative
Itamar Ravid - @itrvd 26
Safe state machines
Problem: I want to safely model an authentication protocol:
case class Connection()
trait Authentication {
def connect(): IO[Connection]
def handshake(connection: Connection): IO[Unit]
def sendCreds(connection: Connection): IO[Unit]
def authenticate(connection: Connection): IO[Unit]
}
Itamar Ravid - @itrvd 27
Safe state machines
Sure, I can just use the functions:
val api: Authentication
import api._
for {
conn <- connect()
_ <- handshake(conn)
_ <- authenticate(conn)
_ <- sendCreds(conn)
} yield data
Itamar Ravid - @itrvd 28
Safe state machines
Sure, I can just use the functions:
val api: Authentication
import api._
for {
conn <- connect()
_ <- handshake(conn)
_ <- authenticate(conn)
_ <- sendCreds(conn)
} yield data
But there's a bug here!
Itamar Ravid - @itrvd 29
Safe state machines
Two problems:
1. Nothing enforces the proper call order
2. Carrying the connection around is not ergonomic
Itamar Ravid - @itrvd 30
Safe state machines
Static types to the rescue! We will modify Connection:
case class Handshake()
case class Credentials()
case class Auth()
case class Done()
case class Connection[S]()
Itamar Ravid - @itrvd 31
Safe state machines
And the API:
trait Authentication {
def connect(): IO[Connection[Handshake]]
def handshake(connection: Connection[Handshake]): IO[Connection[Credentials]]
def sendCreds(connection: Connection[Credentials]): IO[Connection[Auth]]
def authenticate(connection: Connection[Auth]): IO[Connection[Done]]
}
Itamar Ravid - @itrvd 32
Safe state machines
Now we need to carry even more connections around:
val api: Authentication
import api._
for {
connHandshake <- connect()
connCreds <- handshake(connHandshake)
connAuth <- sendCreds(connCreds)
connDone <- authenticate(connAuth)
} yield connDone
Safe, but ugly.
Itamar Ravid - @itrvd 33
Safe state machines
FP saves the day! This is IndexedStateT:
case class IndexedStateT[F, SA, SB, A](
run: SA => F[(SB, A)]
)
Itamar Ravid - @itrvd 34
Safe state machines
FP saves the day! This is IndexedStateT:
case class IndexedStateT[F, SA, SB, A](
run: SA => F[(SB, A)]
)
For example:
SA = Connection[Handshake], SB = Connection[Credentials]
F = IO, A = Unit
Itamar Ravid - @itrvd 35
Safe state machines
val handshake: Connection[Handshake] =>
IO[(Connection[Credentials], Unit)]
Itamar Ravid - @itrvd 36
Safe state machines
We can modify our API to use it:
trait Authentication {
def connect: IndexedStateT[IO, Unit, Connection[Handshake], Unit]
def handshake: IndexedStateT[IO, Connection[Handshake], Connection[Credentials], Unit]
def sendCreds: IndexedStateT[IO, Connection[Credentials], Connection[Auth], Unit]
def authenticate: IndexedStateT[IO, Connection[Auth], Connection[Done], Unit]
}
Itamar Ravid - @itrvd 37
Safe state machines
And this is how the usage looks like:
val api: Authentication
import api._
val authenticate: IndexedStateT[IO, Unit, Connection[Done], Unit] =
for {
_ <- connect
_ <- handshake
_ <- sendCreds
_ <- authenticate
} yield data
Itamar Ravid - @itrvd 38
Safe state machines
The original example, by the way, errors out:
val api: Authentication
import api._
for {
_ <- connect
_ <- handshake
_ <- authenticate
_ <- sendCreds
} yield data
Itamar Ravid - @itrvd 39
Safe state machines
cmd6.sc:6: polymorphic expression cannot be instantiated to expected type;
found : [B, SC]IndexedStateT[IO,Connection[Auth],SC,B]
required: IndexedStateT[IO,Connection[Credentials],?,?]
_ <- authenticate
^
Itamar Ravid - @itrvd 40
Summary
• IndexedStateT is very cool for type-safe transitions
• It is a Monad and an Applicative when SA = SB
• For non-linear transitions: shapeless.Coproduct and
shapeless.HList
• IxStateT in Haskell is in the indexed-extras package
Itamar Ravid - @itrvd 41
Resource allocation and release
Problem: I have many resources allocated in my app.
I need to manage their acquisition and release.
def createKafkaConsumer: IO[KafkaConsumer]
trait KafkaConsumer {
def close: IO[Unit]
}
def createDBConnection: IO[DBConnection]
trait DBConnection {
def close: IO[Unit]
}
Itamar Ravid - @itrvd 42
Resource allocation and release
How should my initialization look like?
Itamar Ravid - @itrvd 43
Resource allocation and release
How should my initialization look like?
def runApp(): IO[Unit] =
for {
consumer <- createKafkaConsumer
conn <- createDBConnection
_ <- doSomething(consumer, conn)
_ <- conn.close
_ <- consumer.close
} yield ()
Itamar Ravid - @itrvd 44
Resource allocation and release
How should my initialization look like?
def runApp(): IO[Unit] =
for {
consumer <- createKafkaConsumer
conn <- createDBConnection
_ <- doSomething(consumer, conn)
_ <- ignoreFailures(conn.close)
_ <- ignoreFailures(consumer.close)
} yield ()
Itamar Ravid - @itrvd 45
The smell is strong with you!
Itamar Ravid - @itrvd 46
Resource allocation and release
If you know Java, you're probably aware of try-with-
resources:
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
This works for any T <: AutoCloseable.
Itamar Ravid - @itrvd 47
Resource allocation and release
Our example would look like this:
try (Consumer consumer = new Consumer()) {
try (DBConnection conn = new DBConnection()) {
doSomething(consumer, conn);
}
}
Itamar Ravid - @itrvd 48
Resource allocation and release
These are not the abstractions you're looking for!
Itamar Ravid - @itrvd 49
Resource allocation and release
IO monads offer us the bracket operation:
object IO {
def bracket[R, A](acquire: IO[R])
(release: R => IO[Unit])
(use: R => IO[A]): IO[A]
}
Itamar Ravid - @itrvd 50
Resource allocation and release
But this gets us to the same place:
def createConsumer: IO[Consumer]
def createDBConnection: IO[DBConnection]
bracket(createConsumer)(_.close) { consumer =>
// use consumer
}
Itamar Ravid - @itrvd 51
Resource allocation and release
But this gets us to the same place:
def createConsumer: IO[Consumer]
def createDBConnection: IO[DBConnection]
bracket(createConsumer)(_.close) { consumer =>
bracket(createDBConnection)(_.close) { conn =>
// use both
}
}
Itamar Ravid - @itrvd 52
Resource allocation and release
There's an interesting data type called Codensity in Scalaz:
abstract class Codensity[F[_], A] {
def apply[B](f: A => F[B]): F[B]
}
Itamar Ravid - @itrvd 53
Resource allocation and release
When we set F = IO, A = R, B = A, apply looks like this:
def apply(f: R => IO[A]): IO[A]
Itamar Ravid - @itrvd 54
Resource allocation and release
This is remarkably similar to part of bracket!
def bracket[R, A](acquire: IO[R])
(release: R => IO[Unit])
(use: R => IO[A]): IO[A]
def apply (f: R => IO[A]): IO[A]
Remember: type signatures are never a coincidence ;-)
Itamar Ravid - @itrvd 55
Resource allocation and release
Let's adapt bracket to fit Codensity:
def resource[R](acquire: IO[R])(release: R => IO[Unit]) =
new Codensity[IO, R] {
def apply[A](use: R => IO[A]): IO[A] =
IO.bracket(acquire)(release)(use)
}
Itamar Ravid - @itrvd 56
Resource allocation and release
So now, when we write a resource, we get back a value:
val consumer: Codensity[IO, Consumer] =
resource(createKafkaConsumer)(_.close)
Itamar Ravid - @itrvd 57
Resource allocation and release
So now, when we write a resource, we get back a value:
val consumer: Codensity[IO, Consumer] =
resource(createKafkaConsumer)(_.close)
And we can use it:
val things: IO[List[Thing]] =
consumer { c =>
c.consumeSomeThings
}
Itamar Ravid - @itrvd 58
Composition
of resources, using Codensity
Itamar Ravid - @itrvd 59
Resource allocation and release
It just so happens that Codensity is a Monad.
So I can do this:
val resources: Codensity[IO, (Consumer, DBConnection)] =
for {
consumer <- resource(createKafkaConsumer)(_.close)
dbConn <- resource(createDBConnection)(_.close)
} yield (consumer, dbConn)
Itamar Ravid - @itrvd 60
Resource allocation and release
Finally, I can do something with these!
val something: IO[Something] =
resources { case (consumer, conn) =>
doSomething(consumer, dbConn)
}
Finalizers will run in reverse order of acquisition.
Itamar Ravid - @itrvd 61
Resource allocation and release
• Codensity is 18 lines of code in total
• I wrote a post on this
• cats-effect includes Resource[F, R]
• In Haskell, you can use ResourceT or Managed
Itamar Ravid - @itrvd 62
Summary
• Don't be afraid of the exotic data types!
• Be on the lookout for examples
Itamar Ravid - @itrvd 63

More Related Content

Similar to Boring Usecases for Exciting Types

The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
Positive Hack Days
 

Similar to Boring Usecases for Exciting Types (20)

Profiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production EnvironmentProfiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production Environment
 
RxJava applied [JavaDay Kyiv 2016]
RxJava applied [JavaDay Kyiv 2016]RxJava applied [JavaDay Kyiv 2016]
RxJava applied [JavaDay Kyiv 2016]
 
Simulation of Signal Reflection in Digital Design
Simulation of Signal Reflection in Digital DesignSimulation of Signal Reflection in Digital Design
Simulation of Signal Reflection in Digital Design
 
Valerii Vasylkov Erlang. measurements and benefits.
Valerii Vasylkov Erlang. measurements and benefits.Valerii Vasylkov Erlang. measurements and benefits.
Valerii Vasylkov Erlang. measurements and benefits.
 
SE2016 Exotic Valerii Vasylkov "Erlang. Measurements and benefits"
SE2016 Exotic Valerii Vasylkov "Erlang. Measurements and benefits"SE2016 Exotic Valerii Vasylkov "Erlang. Measurements and benefits"
SE2016 Exotic Valerii Vasylkov "Erlang. Measurements and benefits"
 
Life Cycle of Metrics, Alerting, and Performance Monitoring in Microservices
Life Cycle of Metrics, Alerting, and Performance Monitoring in MicroservicesLife Cycle of Metrics, Alerting, and Performance Monitoring in Microservices
Life Cycle of Metrics, Alerting, and Performance Monitoring in Microservices
 
Creating and Using the Flux SQL Datasource | Katy Farmer | InfluxData
Creating and Using the Flux SQL Datasource | Katy Farmer | InfluxData Creating and Using the Flux SQL Datasource | Katy Farmer | InfluxData
Creating and Using the Flux SQL Datasource | Katy Farmer | InfluxData
 
Maitri's Matlab
Maitri's Matlab   Maitri's Matlab
Maitri's Matlab
 
Balaji Palani [InfluxData] | InfluxDB Tasks Overview | InfluxDays 2022
Balaji Palani [InfluxData] | InfluxDB Tasks Overview | InfluxDays 2022Balaji Palani [InfluxData] | InfluxDB Tasks Overview | InfluxDays 2022
Balaji Palani [InfluxData] | InfluxDB Tasks Overview | InfluxDays 2022
 
Joel Falcou, Boost.SIMD
Joel Falcou, Boost.SIMDJoel Falcou, Boost.SIMD
Joel Falcou, Boost.SIMD
 
OSA Con 2022 - Specifics of data analysis in Time Series Databases - Roman Kh...
OSA Con 2022 - Specifics of data analysis in Time Series Databases - Roman Kh...OSA Con 2022 - Specifics of data analysis in Time Series Databases - Roman Kh...
OSA Con 2022 - Specifics of data analysis in Time Series Databases - Roman Kh...
 
The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
The System of Automatic Searching for Vulnerabilities or how to use Taint Ana...
 
ParallelProgrammingBasics_v2.pdf
ParallelProgrammingBasics_v2.pdfParallelProgrammingBasics_v2.pdf
ParallelProgrammingBasics_v2.pdf
 
CS304PC:Computer Organization and Architecture Session 33 demo 1 ppt.pdf
CS304PC:Computer Organization and Architecture  Session 33 demo 1 ppt.pdfCS304PC:Computer Organization and Architecture  Session 33 demo 1 ppt.pdf
CS304PC:Computer Organization and Architecture Session 33 demo 1 ppt.pdf
 
Parallel Processing Techniques Pipelining
Parallel Processing Techniques PipeliningParallel Processing Techniques Pipelining
Parallel Processing Techniques Pipelining
 
Accelerating analytics on the Sensor and IoT Data.
Accelerating analytics on the Sensor and IoT Data. Accelerating analytics on the Sensor and IoT Data.
Accelerating analytics on the Sensor and IoT Data.
 
fog or: How I Learned to Stop Worrying and Love the Cloud
fog or: How I Learned to Stop Worrying and Love the Cloudfog or: How I Learned to Stop Worrying and Love the Cloud
fog or: How I Learned to Stop Worrying and Love the Cloud
 
Business Dashboards using Bonobo ETL, Grafana and Apache Airflow
Business Dashboards using Bonobo ETL, Grafana and Apache AirflowBusiness Dashboards using Bonobo ETL, Grafana and Apache Airflow
Business Dashboards using Bonobo ETL, Grafana and Apache Airflow
 
Where the wild things are - Benchmarking and Micro-Optimisations
Where the wild things are - Benchmarking and Micro-OptimisationsWhere the wild things are - Benchmarking and Micro-Optimisations
Where the wild things are - Benchmarking and Micro-Optimisations
 
Jay Clifford [InfluxData] | Tips & Tricks for Analyzing IIoT in Real-Time | I...
Jay Clifford [InfluxData] | Tips & Tricks for Analyzing IIoT in Real-Time | I...Jay Clifford [InfluxData] | Tips & Tricks for Analyzing IIoT in Real-Time | I...
Jay Clifford [InfluxData] | Tips & Tricks for Analyzing IIoT in Real-Time | I...
 

Recently uploaded

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 

Recently uploaded (20)

The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 

Boring Usecases for Exciting Types

  • 1. Boring Usecases for Exciting Types Itamar Ravid - FLIP '18 Itamar Ravid - @itrvd 1
  • 2. About me • Freelance software developer • Doing functional programming, microservices, DevOps • Co-organize the Underscore Scala meetup • Hit me up if you want to work together! Itamar Ravid - @itrvd 2
  • 3. Agenda • Why I wrote this talk • Three data types and their usecases Itamar Ravid - @itrvd 3
  • 5. Itamar Ravid - @itrvd 5
  • 6. How do we grow as functional programmers Itamar Ravid - @itrvd 6
  • 7. Classic "draw an owl" problem How do we go from this: quicksort [] = [] quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater) where lesser = filter (< p) xs greater = filter ( >= p) xs Itamar Ravid - @itrvd 7
  • 8. Classic "draw an owl" problem To this: -- | Fokkinga's postpromorphism postpro :: Recursive t => (forall b. Base t b -> Base t b) -- natural transformation -> (a -> Base t a) -- a (Base t)-coalgebra -> a -- seed -> t postpro e g = a where a = embed . fmap (ana (e . project) . a) . g Itamar Ravid - @itrvd 8
  • 10. Examples We are sorely lacking! Itamar Ravid - @itrvd 10
  • 11. Actual agenda Purely functional and type-safe: • Time measurement and instrumentation • State machines • Resource allocation and release • Let's go! Itamar Ravid - @itrvd 11
  • 12. Examples ahead assume: import cats._, cats.data._, cats.implicits._ import cats.effect._ Itamar Ravid - @itrvd 12
  • 13. Time measurement and instrumentation Problem: I have a bunch of computations, and I want to measure them. def computeCv(data: Data): IO[Double] = for { sample <- computeSample(data) mean <- computeMean(sample) variance <- computeVariance(sample) } yield mean / variance Itamar Ravid - @itrvd 13
  • 14. Time measurement and instrumentation Non-solution: manually instrument. def computeCv(data: Data): IO[Double] = for { startTime <- time() sample <- computeSample(data) sampleTime <- time() mean <- computeMean(sample) meanTime <- time() variance <- computeVariance(sample) varTime <- time() } yield mean / variance Itamar Ravid - @itrvd 14
  • 15. Itamar Ravid - @itrvd 15
  • 16. Time measurement and instrumentation To start, we define a timed combinator: def timed[A](fa: IO[A]): IO[(FiniteDuration, A)] = for { startTime <- time() a <- fa endTime <- time() } yield ((endTime - startTime).millis, a) fa is pure, so we can just pass it around. Itamar Ravid - @itrvd 16
  • 17. Time measurement and instrumentation We can also name this computation: def timed[A](fa: IO[A], name: String): IO[((String, FiniteDuration), A)] = for { startTime <- time() a <- fa endTime <- time() } yield (name -> (endTime - startTime).millis, a) Itamar Ravid - @itrvd 17
  • 18. Time measurement and instrumentation We can now use our new timed function: def computeCv(data: Data): IO[Double] = for { (sampleTime, sample) <- timed(computeSample(data), "sample") (meanTime, mean) <- timed(computeMean(sample), "mean") (varTime, variance) <- timed(computeVariance(sample), "var") } yield mean / variance Kinda tedious though! Itamar Ravid - @itrvd 18
  • 19. Time measurement and instrumentation To save us from that bookeeping, comes WriterT: case class WriterT[F[_], Log, Value]( run: F[(Log, Value)] ) Itamar Ravid - @itrvd 19
  • 20. Time measurement and instrumentation To save us from that bookeeping, comes WriterT: case class WriterT[F[_], Log, Value]( run: F[(Log, Value)] ) If Log: Semigroup- we can merge logs. Itamar Ravid - @itrvd 20
  • 21. Time measurement and instrumentation We modify timed to return a WriterT: type Timing = Map[String, FiniteDuration] def timed(name: String, fa: IO[A]): WriterT[IO, Timing, A] = WriterT { for { startTime <- time() a <- fa endTime <- time() } yield (Map(name -> (endTime - startTime).millis), a) } Itamar Ravid - @itrvd 21
  • 22. Time measurement and instrumentation And we can call this Timed[A] for nicety: type Timed[A] = WriterT[IO, Map[String, FiniteDuration], A] def timed(name: String, fa: IO[A]): Timed[A] = WriterT { for { startTime <- time() a <- fa endTime <- time() } yield (Map(name -> (endTime - startTime).millis), a) } Itamar Ravid - @itrvd 22
  • 23. Time measurement and instrumentation Our original program is only slightly changed: def computeCv(data: Data): Timed[Double] = for { sample <- timed(computeSample(data), "sample") mean <- timed(computeMean(sample), "mean") variance <- timed(computeVariance(sample), "var") } yield mean / variance Itamar Ravid - @itrvd 23
  • 24. Time measurement and instrumentation To unpack the timing data, we can define another function: def logTimings[A](timed: Timed[A]): IO[A] = for { (timing, a) <- timed.run _ <- IO(println(timing)) } yield a Itamar Ravid - @itrvd 24
  • 25. Time measurement and instrumentation And use it: def computeCv(data: Data): IO[Double] = logTimings { for { sample <- timed(computeSample(data), "sample") mean <- timed(computeMean(sample), "mean") variance <- timed(computeVariance(sample), "var") } yield mean / variance } Itamar Ravid - @itrvd 25
  • 26. Summary • You can add infix syntax using implicit class for nicety: computeSample(data).timed("sample").logTimings, etc. • WriterT is a Monad and an Applicative Itamar Ravid - @itrvd 26
  • 27. Safe state machines Problem: I want to safely model an authentication protocol: case class Connection() trait Authentication { def connect(): IO[Connection] def handshake(connection: Connection): IO[Unit] def sendCreds(connection: Connection): IO[Unit] def authenticate(connection: Connection): IO[Unit] } Itamar Ravid - @itrvd 27
  • 28. Safe state machines Sure, I can just use the functions: val api: Authentication import api._ for { conn <- connect() _ <- handshake(conn) _ <- authenticate(conn) _ <- sendCreds(conn) } yield data Itamar Ravid - @itrvd 28
  • 29. Safe state machines Sure, I can just use the functions: val api: Authentication import api._ for { conn <- connect() _ <- handshake(conn) _ <- authenticate(conn) _ <- sendCreds(conn) } yield data But there's a bug here! Itamar Ravid - @itrvd 29
  • 30. Safe state machines Two problems: 1. Nothing enforces the proper call order 2. Carrying the connection around is not ergonomic Itamar Ravid - @itrvd 30
  • 31. Safe state machines Static types to the rescue! We will modify Connection: case class Handshake() case class Credentials() case class Auth() case class Done() case class Connection[S]() Itamar Ravid - @itrvd 31
  • 32. Safe state machines And the API: trait Authentication { def connect(): IO[Connection[Handshake]] def handshake(connection: Connection[Handshake]): IO[Connection[Credentials]] def sendCreds(connection: Connection[Credentials]): IO[Connection[Auth]] def authenticate(connection: Connection[Auth]): IO[Connection[Done]] } Itamar Ravid - @itrvd 32
  • 33. Safe state machines Now we need to carry even more connections around: val api: Authentication import api._ for { connHandshake <- connect() connCreds <- handshake(connHandshake) connAuth <- sendCreds(connCreds) connDone <- authenticate(connAuth) } yield connDone Safe, but ugly. Itamar Ravid - @itrvd 33
  • 34. Safe state machines FP saves the day! This is IndexedStateT: case class IndexedStateT[F, SA, SB, A]( run: SA => F[(SB, A)] ) Itamar Ravid - @itrvd 34
  • 35. Safe state machines FP saves the day! This is IndexedStateT: case class IndexedStateT[F, SA, SB, A]( run: SA => F[(SB, A)] ) For example: SA = Connection[Handshake], SB = Connection[Credentials] F = IO, A = Unit Itamar Ravid - @itrvd 35
  • 36. Safe state machines val handshake: Connection[Handshake] => IO[(Connection[Credentials], Unit)] Itamar Ravid - @itrvd 36
  • 37. Safe state machines We can modify our API to use it: trait Authentication { def connect: IndexedStateT[IO, Unit, Connection[Handshake], Unit] def handshake: IndexedStateT[IO, Connection[Handshake], Connection[Credentials], Unit] def sendCreds: IndexedStateT[IO, Connection[Credentials], Connection[Auth], Unit] def authenticate: IndexedStateT[IO, Connection[Auth], Connection[Done], Unit] } Itamar Ravid - @itrvd 37
  • 38. Safe state machines And this is how the usage looks like: val api: Authentication import api._ val authenticate: IndexedStateT[IO, Unit, Connection[Done], Unit] = for { _ <- connect _ <- handshake _ <- sendCreds _ <- authenticate } yield data Itamar Ravid - @itrvd 38
  • 39. Safe state machines The original example, by the way, errors out: val api: Authentication import api._ for { _ <- connect _ <- handshake _ <- authenticate _ <- sendCreds } yield data Itamar Ravid - @itrvd 39
  • 40. Safe state machines cmd6.sc:6: polymorphic expression cannot be instantiated to expected type; found : [B, SC]IndexedStateT[IO,Connection[Auth],SC,B] required: IndexedStateT[IO,Connection[Credentials],?,?] _ <- authenticate ^ Itamar Ravid - @itrvd 40
  • 41. Summary • IndexedStateT is very cool for type-safe transitions • It is a Monad and an Applicative when SA = SB • For non-linear transitions: shapeless.Coproduct and shapeless.HList • IxStateT in Haskell is in the indexed-extras package Itamar Ravid - @itrvd 41
  • 42. Resource allocation and release Problem: I have many resources allocated in my app. I need to manage their acquisition and release. def createKafkaConsumer: IO[KafkaConsumer] trait KafkaConsumer { def close: IO[Unit] } def createDBConnection: IO[DBConnection] trait DBConnection { def close: IO[Unit] } Itamar Ravid - @itrvd 42
  • 43. Resource allocation and release How should my initialization look like? Itamar Ravid - @itrvd 43
  • 44. Resource allocation and release How should my initialization look like? def runApp(): IO[Unit] = for { consumer <- createKafkaConsumer conn <- createDBConnection _ <- doSomething(consumer, conn) _ <- conn.close _ <- consumer.close } yield () Itamar Ravid - @itrvd 44
  • 45. Resource allocation and release How should my initialization look like? def runApp(): IO[Unit] = for { consumer <- createKafkaConsumer conn <- createDBConnection _ <- doSomething(consumer, conn) _ <- ignoreFailures(conn.close) _ <- ignoreFailures(consumer.close) } yield () Itamar Ravid - @itrvd 45
  • 46. The smell is strong with you! Itamar Ravid - @itrvd 46
  • 47. Resource allocation and release If you know Java, you're probably aware of try-with- resources: try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } This works for any T <: AutoCloseable. Itamar Ravid - @itrvd 47
  • 48. Resource allocation and release Our example would look like this: try (Consumer consumer = new Consumer()) { try (DBConnection conn = new DBConnection()) { doSomething(consumer, conn); } } Itamar Ravid - @itrvd 48
  • 49. Resource allocation and release These are not the abstractions you're looking for! Itamar Ravid - @itrvd 49
  • 50. Resource allocation and release IO monads offer us the bracket operation: object IO { def bracket[R, A](acquire: IO[R]) (release: R => IO[Unit]) (use: R => IO[A]): IO[A] } Itamar Ravid - @itrvd 50
  • 51. Resource allocation and release But this gets us to the same place: def createConsumer: IO[Consumer] def createDBConnection: IO[DBConnection] bracket(createConsumer)(_.close) { consumer => // use consumer } Itamar Ravid - @itrvd 51
  • 52. Resource allocation and release But this gets us to the same place: def createConsumer: IO[Consumer] def createDBConnection: IO[DBConnection] bracket(createConsumer)(_.close) { consumer => bracket(createDBConnection)(_.close) { conn => // use both } } Itamar Ravid - @itrvd 52
  • 53. Resource allocation and release There's an interesting data type called Codensity in Scalaz: abstract class Codensity[F[_], A] { def apply[B](f: A => F[B]): F[B] } Itamar Ravid - @itrvd 53
  • 54. Resource allocation and release When we set F = IO, A = R, B = A, apply looks like this: def apply(f: R => IO[A]): IO[A] Itamar Ravid - @itrvd 54
  • 55. Resource allocation and release This is remarkably similar to part of bracket! def bracket[R, A](acquire: IO[R]) (release: R => IO[Unit]) (use: R => IO[A]): IO[A] def apply (f: R => IO[A]): IO[A] Remember: type signatures are never a coincidence ;-) Itamar Ravid - @itrvd 55
  • 56. Resource allocation and release Let's adapt bracket to fit Codensity: def resource[R](acquire: IO[R])(release: R => IO[Unit]) = new Codensity[IO, R] { def apply[A](use: R => IO[A]): IO[A] = IO.bracket(acquire)(release)(use) } Itamar Ravid - @itrvd 56
  • 57. Resource allocation and release So now, when we write a resource, we get back a value: val consumer: Codensity[IO, Consumer] = resource(createKafkaConsumer)(_.close) Itamar Ravid - @itrvd 57
  • 58. Resource allocation and release So now, when we write a resource, we get back a value: val consumer: Codensity[IO, Consumer] = resource(createKafkaConsumer)(_.close) And we can use it: val things: IO[List[Thing]] = consumer { c => c.consumeSomeThings } Itamar Ravid - @itrvd 58
  • 59. Composition of resources, using Codensity Itamar Ravid - @itrvd 59
  • 60. Resource allocation and release It just so happens that Codensity is a Monad. So I can do this: val resources: Codensity[IO, (Consumer, DBConnection)] = for { consumer <- resource(createKafkaConsumer)(_.close) dbConn <- resource(createDBConnection)(_.close) } yield (consumer, dbConn) Itamar Ravid - @itrvd 60
  • 61. Resource allocation and release Finally, I can do something with these! val something: IO[Something] = resources { case (consumer, conn) => doSomething(consumer, dbConn) } Finalizers will run in reverse order of acquisition. Itamar Ravid - @itrvd 61
  • 62. Resource allocation and release • Codensity is 18 lines of code in total • I wrote a post on this • cats-effect includes Resource[F, R] • In Haskell, you can use ResourceT or Managed Itamar Ravid - @itrvd 62
  • 63. Summary • Don't be afraid of the exotic data types! • Be on the lookout for examples Itamar Ravid - @itrvd 63