SlideShare a Scribd company logo
SUPERCHARGED IMPERATIVE
PROGRAMMING WITH HASKELL
AND FP
ANUPAM JAIN
2
Hello!
3
❑ HOME PAGE

https://fpncr.github.io
❑ GETTOGETHER COMMUNITY

https://gettogether.community/fpncr/
❑ MEETUP

https://www.meetup.com/DelhiNCR-Haskell-And-
Functional-Programming-Languages-Group
❑ TELEGRAM:

https://t.me/fpncr
Functional Programming NCR
4
❑Type safety. Eliminates a large class of errors.
❑Effectful values are first class
❑Higher Order Patterns
❑Reduction in Boilerplate
❑Zero Cost Code Reuse
Overview
5
❑Order of operations matters
❑Contrast with functional, where the order of
operations does not matter.
Define “Imperative”
6
write "Do you want a pizza?”
if (read() == "Yes") orderPizza()
write "Should I launch missiles?”
if (read() == "Yes") launchMissiles()
Imperative is simple
7
write "Do you want a pizza?”
if (read() == "Yes") orderPizza()
write "Should I launch missiles?”
if (read() == "Yes") launchMissiles()
Imperative is simple
You REALLY DON’T
want to do these
out of order
8
do
write "Do you want a pizza?"
canOrder <- read
When (canOrder == "Yes") orderPizza
write "Should I launch missiles?"
canLaunch <- read
When (canLaunch == "Yes") launchMissiles
Functional?
9
do
write "Do you want a pizza?"
canOrder <- read
when (canOrder == "Yes") orderPizza
write "Should I launch missiles?"
canLaunch <- read
when (canLaunch == "Yes") launchMissiles
Functional?
Haskell
10
write "Do you want a pizza?" >>= _ ->
read >>= canOrderPizza ->
if (canOrderPizza == "Yes") then
orderPizza
else pure () >>= _ ->
write "Should I launch missiles?" >>= _ -
>
read >>= canLaunchMissiles ->
if (canLaunchMissiles == "Yes") then
launchMissiles
else pure ()
Functional?
11
plusOne = x -> x+1
add = x -> y -> x+y
A bit of syntax
Lambdas
12
(>>=) = effect -> handler -> ...
A bit of syntax
Operators
13
read >>= canOrderPizza -> ...
A bit of syntax
Infix Usage
14
write "Do you want a pizza?" >>= _ ->
read >>= canOrderPizza ->
if (canOrderPizza == "Yes") then
orderPizza
else pure ()
One At a Time
15
write "Should I launch missiles?" >>= _ ->
read >>= canLaunchMissiles ->
if (canLaunchMissiles == "Yes") then
launchMissiles
else pure ()
One At a Time
16
handlePizza >>= _ ->
handleMissiles
Together
17
handlePizza >>= _ ->
handleMissiles
Together
18
handlePizza :: IO ()
handlePizza = do
write "Do you want a pizza?"
canOrderPizza <- read
if (canOrderPizza == "Yes")
then orderPizza
else pure ()
Types This entire block
1. Is Effectful
2. Returns ()
19
Effectful Logic
Pure Logic
Outside World
20
❑Can’t mix effectful (imperative) code with pure
(functional) code
❑All branches must have the same return type
Types
21
SideEffects!!
22
“Haskell” is the world’s finest
imperative programming
language.
~Simon Peyton Jones
(Creator of Haskell)
23
So How is Haskell
The Best Imperative
Programming
Language?
24
❑We don’t launch nukes without ordering pizza
Change Requirements
25
handlePizza :: IO Bool
handlePizza = do
write "Do you want a pizza?"
canOrderPizza <- read
if (canOrderPizza == "Yes")
then orderPizza >> pure true
else pure false
Types
26
do
pizzaOrdered <- handlePizza
if pizzaOrdered
then handleMissiles
else pure ()
With Changed
Requirements
27
❑Ask the user a bunch of questions
❑Then perform a bunch of actions
Reorder?
28
Must Rearchitect
do
write "Do you want a pizza?"
canOrder <- read
write "Should I launch missiles?"
canLaunch <- read
when (canOrder == "Yes") orderPizza
when (canLaunch == "Yes") launchMissiles
29
Must Rearchitect
do
write "Do you want a pizza?"
canOrder <- read
write "Should I launch missiles?"
canLaunch <- read
when (canOrder == "Yes") orderPizza
when (canLaunch == "Yes") launchMissiles
But we have lost
the separation
between
Ordering pizza
and Launching nukes
30
We Need
❑Define complex flows with user input and a final
effect to be performed
❑To compose these flows without boilerplate
❑Be able to run the final effects together at the end
of all user input
31
Desired Abstraction
handlePizza = ...
handleNukes = ...
do
handlePizza
handleNukes
We ask questions in this order,
but the final effect of ordering pizza
and launching nukes should only
happen together at the end
32
Must Rearchitect
handlePizza = do
write "Do you want a pizza?"
canOrder <- read
return $
when (canOrder == "Yes") orderPizza
33
Must Rearchitect
handlePizza :: IO (IO ())
handlePizza = do
write "Do you want a pizza?"
canOrder <- read
return $
when (canOrder == "Yes") orderPizza
Return value is a CLOSURE
Captures `canOrder`
34
Must Rearchitect
handleNukes :: IO (IO ())
handleNukes = do
write “Should I launch nukes?"
canLaunch <- read
return $
when (canLaunch == "Yes") launchNukes
Return value is a CLOSURE
Captures `canLaunch`
35
Compose together
do
pizzaEffect <- handlePizza
nukeEffect <- handleNukes
pizzaEffect

nukeEffect
36
Generalises?
This looks very boilerplaty
do
pizzaEffect <- handlePizza
nukeEffect <- handleNukes
...
pizzaEffect

nukeEffect
...
37
Desired Interface
finalEffect =

handlePizza AND

handleNukes AND
...
38
And Allow A Way to
specify “No Effects”
finalEffect = emptyEffects
39
Looks Like a Monoid!
class Monoid M where
empty :: M

(<>) :: M -> M -> M
40
IO already is a
Monoid!
❑What happens when we do the following?
handlePizza <> handleNukes
41
IO already is a
Monoid!
instance Monoid a => Monoid (IO a) where
empty = pure empty
f <> g = do
a <- f
b <- g
pure (a <> b)
42
IO already is a
Monoid!
instance Monoid a => Monoid (IO a) where
empty = pure empty
f <> g = do
a <- f
b <- g
pure (a <> b)
First perform individual effects
43
IO already is a
Monoid!
instance Monoid a => Monoid (IO a) where
empty = pure empty
f <> g = do
a <- f
b <- g
pure (a <> b) Then Join the results
As Monoids
44
IO already is a
Monoid!
❑So this does the right thing!
do
finalEffects <- handlePizza <> handleNukes
finalEffects
45
This is also a pattern
join :: Monad M => M (M a) -> M a
join :: IO (IO a) -> IO a
join (handlePizza <> handleNukes)
46
No Boilerplate!
join :: Monad M => M (M a) -> M a
join :: IO (IO a) -> IO a
join (handlePizza <> handleNukes)
47
Final Code

handlePizza
handlePizza :: IO (IO ())
handlePizza = do
write "Do you want a pizza?"
canOrder <- read
return $
when (canOrder == "Yes") orderPizza
48
Final Code

handleNukes
handleNukes :: IO (IO ())
handleNukes = do
write “Should I launch nukes?"
canLaunch <- read
return $
when (canLaunch == "Yes") launchNukes
49
Final Code

Combine flows together
join (handlePizza <> handleNukes <> ...)
join (mappend [ handlePizza
, handleNukes
...
])
Or Perhaps
50
❑We don’t launch nukes without ordering pizza
❑We don’t order pizza when not launching nukes
Change Requirements
Again
51
Must Rearchitect
do
write "Do you want a pizza?"
canOrder <- read
write "Should I launch missiles?"
canLaunch <- read
when (canOrder == “Yes" && canLaunch ==
"Yes") (orderPizza >> launchMissiles)
52
Must Rearchitect
do
write "Do you want a pizza?"
canOrder <- read
write "Should I launch missiles?"
canLaunch <- read
when (canOrder == “Yes" && canLaunch ==
"Yes") (orderPizza >> launchMissiles)
Business Logic
53
A General Pattern
do
write “Question 1 ...”
answer1 <- read
...
when (validates answer1 ...)
performAllEffects
54
We Need
❑Define complex flows with user input and a final
effect to be performed
❑To compose these flows without boilerplate
❑Call a function on all the user input to determine if
we should perform the final effects.
❑Be able to run the final effects together at the end
of all user input
55
Can we do this with
Monoids?
do
finalEffects <- handlePizza <> handleNukes
finalEffects
❑We abstracted away the captured variables
❑Now all we can do is run the final composed effect
We can’t access `canOrder` or `canLaunch` here
56
FP Gives you
Granularly
Powerful
Abstractions
❑Monads are too powerful (i.e. boilerplate)
❑Monoids abstract away too much
❑Need something in the middle
57
Let's work through this
data Ret a = Ret
{ input :: a
, effect :: IO ()
}
❑Return the final effect, AND the user input
❑Parameterise User Input as `a`
58
Let's work through this
handlePizza :: IO (Ret Boolean)
handlePizza = do
write "Do you want a pizza?"
canOrder <- read
return $ Ret canOrder $
when (canOrder == "Yes") orderPizza
59
Compose Effects
do
retPizza <- handlePizza
retNuke <- handleNuke
when valid (input retPizza) (input
retNuke) do
effect retPizza
effect retNuke
60
Compose Effects
do
retPizza <- handlePizza
retNuke <- handleNuke
when valid (input retPizza) (input
retNuke) do
effect retPizza
effect retNuke
UGH! Boilerplate!
61
Compose Effects
do
retPizza <- handlePizza
retNuke <- handleNuke
let go = valid (input retPizza) (input
retNuke)
when go do
effect retPizza
effect retNuke
62
Compose Effects
do
retPizza <- handlePizza
retNuke <- handleNuke
let go = valid (input retPizza) (input
retNuke)
when go do
effect retPizza
effect retNuke
Applicative!
63
IO is an Applicative
instance Applicative IO where
f <*> a = do
f' <- f
a' <- a
pure (f' a')
64
Try to Use Applicative IO
do
go <- valid
<$> (input <$> handlePizza)
<*> (input <$> handleNuke)
when go do
effect ??retPizza
effect ??retNuke
65
Dial Back a Little
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let go = valid
<$> input retPizza
<*> input retNuke
when go do
effect retPizza
effect retNuke
66
Perhaps a try a
different abstraction
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let go = valid
<$> input retPizza
<*> input retNuke
when go do
effect retPizza
effect retNuke
This is a common pattern
Can we abstract this?
67
Running a Return value
data Ret a = Ret
{ input :: a
, effect :: IO ()}
runRet :: Ret Bool -> IO ()
runRet (Ret b e) = when b e
68
More trouble than its
worth?
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let go = valid
<$> input retPizza
<*> input retNuke
runRet ??? We need to Compose a Ret
To be able to run it
69
However!
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let go = valid
<$> input retPizza
<*> input retNuke
runRet ???
This could return a
Ret instead!
70
Combining Return values
data Ret a = Ret
{ input :: a
, effect :: IO ()}
instance Functor Ret where
fmap f (Ret a e) = Ret (f a) e
instance Applicative Ret where
Ret f e1 <*> Ret a e2 =
Ret (f a) (e1 <> e2)
71
Less Boilerplate!
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let ret = valid
<$> retPizza
<*> retNuke
runRet ret
72
Hmm, Still Boilerplatey
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let ret = valid
<$> retPizza
<*> retNuke
runRet ret
Two Successive
Applicatives
73
Hmm, Still Boilerplatey
do
(retPizza, retNuke) <- (,)
<$> handlePizza
<*> handleNuke
let ret = valid
<$> retPizza
<*> retNuke
runRet ret
Combine Effectful

IO
Combine Effectful

Ret
74
Compose Applicatives?
data IO a = ...
data Ret a = Ret
{ input :: a
, effect :: IO ()}
type Flow a = IO (Ret a)
We need an Applicative instance for Flow
75
Applicatives Compose!
Import Data.Functor.Compose
type Compose f g a = Compose (f (g a))
type Flow a = Compose IO Ret a
76
Applicatives Compose!
instance (Applicative f, Applicative g)
=> Applicative (Compose f g) where
Compose f <*> Compose x =
Compose (liftA2 (<*>) f x)
77
Running Compose
runRet :: Ret Bool -> IO ()
runRet (Ret b e) = when b e
runFlow :: Compose IO Ret Bool -> IO ()
runFlow (Compose e) = e >>= runRet
78
Defining Flows
handlePizza :: Flow Boolean
handlePizza = Compose $ do
write "Do you want a pizza?"
canOrder <- read
return $ Ret canOrder $
when (canOrder == "Yes") orderPizza
79
Composing Flow With
Business Logic
valid
<$> handlePizza
<*> handleNukes
<*> ...
80
No Boilerplate
runFlow $ valid
<$> handlePizza
<*> handleNuke
81
❑Type safety. Eliminates a large class of errors.
❑Effectful values are first class
❑Higher Order Patterns
❑Reduction in Boilerplate
❑Zero Cost Code Reuse
Takeaways
82
SideEffects!!
83
Besties!!
84
Thank You
Questions?

More Related Content

PDF
Effecting Pure Change - How anything ever gets done in functional programming...
PDF
Hierarchical free monads and software design in fp
PDF
pure-functional-programming.pdf
PDF
Principled io in_scala_2019_distribution
PDF
Purely Functional I/O
PDF
It's the end of design patterns as we know it (and i feel fine)
PPT
Haskell retrospective
PDF
The Death of Final Tagless
Effecting Pure Change - How anything ever gets done in functional programming...
Hierarchical free monads and software design in fp
pure-functional-programming.pdf
Principled io in_scala_2019_distribution
Purely Functional I/O
It's the end of design patterns as we know it (and i feel fine)
Haskell retrospective
The Death of Final Tagless

Similar to Supercharged imperative programming with Haskell and Functional Programming (20)

PPTX
Pure Functional Effects - from IO to ZIO
PDF
Sync considered unethical
PPTX
Intro to Functional Programming
PDF
Model-based programming and AI-assisted software development
PDF
PDF
Functional programming
PDF
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
PDF
Applicative Functor - Part 2
PDF
Ruslan.shevchenko: most functional-day-kiev 2014
PDF
Interpreter Case Study - Design Patterns
PDF
scalaliftoff2009.pdf
PDF
scalaliftoff2009.pdf
PDF
scalaliftoff2009.pdf
PDF
scalaliftoff2009.pdf
PPTX
Functional programming
PDF
Project FoX: A Tool That Offers Automated Testing Using a Formal Approach
PPTX
Столпы функционального программирования для адептов ООП, Николай Мозговой
PPTX
Good functional programming is good programming
PDF
Scala, just a better java?
PDF
Functional Programming in Scala 1st Edition Paul Chiusano
Pure Functional Effects - from IO to ZIO
Sync considered unethical
Intro to Functional Programming
Model-based programming and AI-assisted software development
Functional programming
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
Applicative Functor - Part 2
Ruslan.shevchenko: most functional-day-kiev 2014
Interpreter Case Study - Design Patterns
scalaliftoff2009.pdf
scalaliftoff2009.pdf
scalaliftoff2009.pdf
scalaliftoff2009.pdf
Functional programming
Project FoX: A Tool That Offers Automated Testing Using a Formal Approach
Столпы функционального программирования для адептов ООП, Николай Мозговой
Good functional programming is good programming
Scala, just a better java?
Functional Programming in Scala 1st Edition Paul Chiusano
Ad

More from Tech Triveni (20)

PDF
UI Dev in Big data world using open source
PDF
Why should a Java programmer shifts towards Functional Programming Paradigm
PDF
Reactive - Is it really a Magic Pill?
PDF
Let’s go reactive with JAVA
PDF
Tackling Asynchrony with Kotlin Coroutines
PDF
Programmatic Ad Tracking: Let the power of Reactive Microservices do talking
PDF
Let's refine your Scala Code
PDF
Observability at scale with Neural Networks: A more proactive approach
PDF
Semi-Supervised Insight Generation from Petabyte Scale Text Data
PDF
Finding the best solution for Image Processing
PDF
Proximity Targeting at Scale using Big Data Platforms
PDF
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
PDF
Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...
PDF
Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)
PDF
Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...
PDF
UX in Big Data Analytics - Paramjit Jolly (Guavus)
PDF
Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)
PDF
Micro Frontends Architecture - Jitendra kumawat (Guavus)
PDF
Apache CarbonData+Spark to realize data convergence and Unified high performa...
PPTX
Micro Frontends Architecture - Jitendra kumawat (Guavus)
UI Dev in Big data world using open source
Why should a Java programmer shifts towards Functional Programming Paradigm
Reactive - Is it really a Magic Pill?
Let’s go reactive with JAVA
Tackling Asynchrony with Kotlin Coroutines
Programmatic Ad Tracking: Let the power of Reactive Microservices do talking
Let's refine your Scala Code
Observability at scale with Neural Networks: A more proactive approach
Semi-Supervised Insight Generation from Petabyte Scale Text Data
Finding the best solution for Image Processing
Proximity Targeting at Scale using Big Data Platforms
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...
Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)
Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...
UX in Big Data Analytics - Paramjit Jolly (Guavus)
Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)
Micro Frontends Architecture - Jitendra kumawat (Guavus)
Apache CarbonData+Spark to realize data convergence and Unified high performa...
Micro Frontends Architecture - Jitendra kumawat (Guavus)
Ad

Recently uploaded (20)

PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Spectroscopy.pptx food analysis technology
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Empathic Computing: Creating Shared Understanding
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Encapsulation theory and applications.pdf
PPTX
Big Data Technologies - Introduction.pptx
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Machine learning based COVID-19 study performance prediction
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
KodekX | Application Modernization Development
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Spectroscopy.pptx food analysis technology
Spectral efficient network and resource selection model in 5G networks
Per capita expenditure prediction using model stacking based on satellite ima...
sap open course for s4hana steps from ECC to s4
Empathic Computing: Creating Shared Understanding
Digital-Transformation-Roadmap-for-Companies.pptx
Unlocking AI with Model Context Protocol (MCP)
Encapsulation theory and applications.pdf
Big Data Technologies - Introduction.pptx
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Programs and apps: productivity, graphics, security and other tools
Encapsulation_ Review paper, used for researhc scholars
Machine learning based COVID-19 study performance prediction
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
KodekX | Application Modernization Development

Supercharged imperative programming with Haskell and Functional Programming

  • 1. SUPERCHARGED IMPERATIVE PROGRAMMING WITH HASKELL AND FP ANUPAM JAIN
  • 3. 3 ❑ HOME PAGE
 https://fpncr.github.io ❑ GETTOGETHER COMMUNITY
 https://gettogether.community/fpncr/ ❑ MEETUP
 https://www.meetup.com/DelhiNCR-Haskell-And- Functional-Programming-Languages-Group ❑ TELEGRAM:
 https://t.me/fpncr Functional Programming NCR
  • 4. 4 ❑Type safety. Eliminates a large class of errors. ❑Effectful values are first class ❑Higher Order Patterns ❑Reduction in Boilerplate ❑Zero Cost Code Reuse Overview
  • 5. 5 ❑Order of operations matters ❑Contrast with functional, where the order of operations does not matter. Define “Imperative”
  • 6. 6 write "Do you want a pizza?” if (read() == "Yes") orderPizza() write "Should I launch missiles?” if (read() == "Yes") launchMissiles() Imperative is simple
  • 7. 7 write "Do you want a pizza?” if (read() == "Yes") orderPizza() write "Should I launch missiles?” if (read() == "Yes") launchMissiles() Imperative is simple You REALLY DON’T want to do these out of order
  • 8. 8 do write "Do you want a pizza?" canOrder <- read When (canOrder == "Yes") orderPizza write "Should I launch missiles?" canLaunch <- read When (canLaunch == "Yes") launchMissiles Functional?
  • 9. 9 do write "Do you want a pizza?" canOrder <- read when (canOrder == "Yes") orderPizza write "Should I launch missiles?" canLaunch <- read when (canLaunch == "Yes") launchMissiles Functional? Haskell
  • 10. 10 write "Do you want a pizza?" >>= _ -> read >>= canOrderPizza -> if (canOrderPizza == "Yes") then orderPizza else pure () >>= _ -> write "Should I launch missiles?" >>= _ - > read >>= canLaunchMissiles -> if (canLaunchMissiles == "Yes") then launchMissiles else pure () Functional?
  • 11. 11 plusOne = x -> x+1 add = x -> y -> x+y A bit of syntax Lambdas
  • 12. 12 (>>=) = effect -> handler -> ... A bit of syntax Operators
  • 13. 13 read >>= canOrderPizza -> ... A bit of syntax Infix Usage
  • 14. 14 write "Do you want a pizza?" >>= _ -> read >>= canOrderPizza -> if (canOrderPizza == "Yes") then orderPizza else pure () One At a Time
  • 15. 15 write "Should I launch missiles?" >>= _ -> read >>= canLaunchMissiles -> if (canLaunchMissiles == "Yes") then launchMissiles else pure () One At a Time
  • 16. 16 handlePizza >>= _ -> handleMissiles Together
  • 17. 17 handlePizza >>= _ -> handleMissiles Together
  • 18. 18 handlePizza :: IO () handlePizza = do write "Do you want a pizza?" canOrderPizza <- read if (canOrderPizza == "Yes") then orderPizza else pure () Types This entire block 1. Is Effectful 2. Returns ()
  • 20. 20 ❑Can’t mix effectful (imperative) code with pure (functional) code ❑All branches must have the same return type Types
  • 22. 22 “Haskell” is the world’s finest imperative programming language. ~Simon Peyton Jones (Creator of Haskell)
  • 23. 23 So How is Haskell The Best Imperative Programming Language?
  • 24. 24 ❑We don’t launch nukes without ordering pizza Change Requirements
  • 25. 25 handlePizza :: IO Bool handlePizza = do write "Do you want a pizza?" canOrderPizza <- read if (canOrderPizza == "Yes") then orderPizza >> pure true else pure false Types
  • 26. 26 do pizzaOrdered <- handlePizza if pizzaOrdered then handleMissiles else pure () With Changed Requirements
  • 27. 27 ❑Ask the user a bunch of questions ❑Then perform a bunch of actions Reorder?
  • 28. 28 Must Rearchitect do write "Do you want a pizza?" canOrder <- read write "Should I launch missiles?" canLaunch <- read when (canOrder == "Yes") orderPizza when (canLaunch == "Yes") launchMissiles
  • 29. 29 Must Rearchitect do write "Do you want a pizza?" canOrder <- read write "Should I launch missiles?" canLaunch <- read when (canOrder == "Yes") orderPizza when (canLaunch == "Yes") launchMissiles But we have lost the separation between Ordering pizza and Launching nukes
  • 30. 30 We Need ❑Define complex flows with user input and a final effect to be performed ❑To compose these flows without boilerplate ❑Be able to run the final effects together at the end of all user input
  • 31. 31 Desired Abstraction handlePizza = ... handleNukes = ... do handlePizza handleNukes We ask questions in this order, but the final effect of ordering pizza and launching nukes should only happen together at the end
  • 32. 32 Must Rearchitect handlePizza = do write "Do you want a pizza?" canOrder <- read return $ when (canOrder == "Yes") orderPizza
  • 33. 33 Must Rearchitect handlePizza :: IO (IO ()) handlePizza = do write "Do you want a pizza?" canOrder <- read return $ when (canOrder == "Yes") orderPizza Return value is a CLOSURE Captures `canOrder`
  • 34. 34 Must Rearchitect handleNukes :: IO (IO ()) handleNukes = do write “Should I launch nukes?" canLaunch <- read return $ when (canLaunch == "Yes") launchNukes Return value is a CLOSURE Captures `canLaunch`
  • 35. 35 Compose together do pizzaEffect <- handlePizza nukeEffect <- handleNukes pizzaEffect
 nukeEffect
  • 36. 36 Generalises? This looks very boilerplaty do pizzaEffect <- handlePizza nukeEffect <- handleNukes ... pizzaEffect
 nukeEffect ...
  • 38. 38 And Allow A Way to specify “No Effects” finalEffect = emptyEffects
  • 39. 39 Looks Like a Monoid! class Monoid M where empty :: M
 (<>) :: M -> M -> M
  • 40. 40 IO already is a Monoid! ❑What happens when we do the following? handlePizza <> handleNukes
  • 41. 41 IO already is a Monoid! instance Monoid a => Monoid (IO a) where empty = pure empty f <> g = do a <- f b <- g pure (a <> b)
  • 42. 42 IO already is a Monoid! instance Monoid a => Monoid (IO a) where empty = pure empty f <> g = do a <- f b <- g pure (a <> b) First perform individual effects
  • 43. 43 IO already is a Monoid! instance Monoid a => Monoid (IO a) where empty = pure empty f <> g = do a <- f b <- g pure (a <> b) Then Join the results As Monoids
  • 44. 44 IO already is a Monoid! ❑So this does the right thing! do finalEffects <- handlePizza <> handleNukes finalEffects
  • 45. 45 This is also a pattern join :: Monad M => M (M a) -> M a join :: IO (IO a) -> IO a join (handlePizza <> handleNukes)
  • 46. 46 No Boilerplate! join :: Monad M => M (M a) -> M a join :: IO (IO a) -> IO a join (handlePizza <> handleNukes)
  • 47. 47 Final Code
 handlePizza handlePizza :: IO (IO ()) handlePizza = do write "Do you want a pizza?" canOrder <- read return $ when (canOrder == "Yes") orderPizza
  • 48. 48 Final Code
 handleNukes handleNukes :: IO (IO ()) handleNukes = do write “Should I launch nukes?" canLaunch <- read return $ when (canLaunch == "Yes") launchNukes
  • 49. 49 Final Code
 Combine flows together join (handlePizza <> handleNukes <> ...) join (mappend [ handlePizza , handleNukes ... ]) Or Perhaps
  • 50. 50 ❑We don’t launch nukes without ordering pizza ❑We don’t order pizza when not launching nukes Change Requirements Again
  • 51. 51 Must Rearchitect do write "Do you want a pizza?" canOrder <- read write "Should I launch missiles?" canLaunch <- read when (canOrder == “Yes" && canLaunch == "Yes") (orderPizza >> launchMissiles)
  • 52. 52 Must Rearchitect do write "Do you want a pizza?" canOrder <- read write "Should I launch missiles?" canLaunch <- read when (canOrder == “Yes" && canLaunch == "Yes") (orderPizza >> launchMissiles) Business Logic
  • 53. 53 A General Pattern do write “Question 1 ...” answer1 <- read ... when (validates answer1 ...) performAllEffects
  • 54. 54 We Need ❑Define complex flows with user input and a final effect to be performed ❑To compose these flows without boilerplate ❑Call a function on all the user input to determine if we should perform the final effects. ❑Be able to run the final effects together at the end of all user input
  • 55. 55 Can we do this with Monoids? do finalEffects <- handlePizza <> handleNukes finalEffects ❑We abstracted away the captured variables ❑Now all we can do is run the final composed effect We can’t access `canOrder` or `canLaunch` here
  • 56. 56 FP Gives you Granularly Powerful Abstractions ❑Monads are too powerful (i.e. boilerplate) ❑Monoids abstract away too much ❑Need something in the middle
  • 57. 57 Let's work through this data Ret a = Ret { input :: a , effect :: IO () } ❑Return the final effect, AND the user input ❑Parameterise User Input as `a`
  • 58. 58 Let's work through this handlePizza :: IO (Ret Boolean) handlePizza = do write "Do you want a pizza?" canOrder <- read return $ Ret canOrder $ when (canOrder == "Yes") orderPizza
  • 59. 59 Compose Effects do retPizza <- handlePizza retNuke <- handleNuke when valid (input retPizza) (input retNuke) do effect retPizza effect retNuke
  • 60. 60 Compose Effects do retPizza <- handlePizza retNuke <- handleNuke when valid (input retPizza) (input retNuke) do effect retPizza effect retNuke UGH! Boilerplate!
  • 61. 61 Compose Effects do retPizza <- handlePizza retNuke <- handleNuke let go = valid (input retPizza) (input retNuke) when go do effect retPizza effect retNuke
  • 62. 62 Compose Effects do retPizza <- handlePizza retNuke <- handleNuke let go = valid (input retPizza) (input retNuke) when go do effect retPizza effect retNuke Applicative!
  • 63. 63 IO is an Applicative instance Applicative IO where f <*> a = do f' <- f a' <- a pure (f' a')
  • 64. 64 Try to Use Applicative IO do go <- valid <$> (input <$> handlePizza) <*> (input <$> handleNuke) when go do effect ??retPizza effect ??retNuke
  • 65. 65 Dial Back a Little do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let go = valid <$> input retPizza <*> input retNuke when go do effect retPizza effect retNuke
  • 66. 66 Perhaps a try a different abstraction do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let go = valid <$> input retPizza <*> input retNuke when go do effect retPizza effect retNuke This is a common pattern Can we abstract this?
  • 67. 67 Running a Return value data Ret a = Ret { input :: a , effect :: IO ()} runRet :: Ret Bool -> IO () runRet (Ret b e) = when b e
  • 68. 68 More trouble than its worth? do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let go = valid <$> input retPizza <*> input retNuke runRet ??? We need to Compose a Ret To be able to run it
  • 69. 69 However! do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let go = valid <$> input retPizza <*> input retNuke runRet ??? This could return a Ret instead!
  • 70. 70 Combining Return values data Ret a = Ret { input :: a , effect :: IO ()} instance Functor Ret where fmap f (Ret a e) = Ret (f a) e instance Applicative Ret where Ret f e1 <*> Ret a e2 = Ret (f a) (e1 <> e2)
  • 71. 71 Less Boilerplate! do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let ret = valid <$> retPizza <*> retNuke runRet ret
  • 72. 72 Hmm, Still Boilerplatey do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let ret = valid <$> retPizza <*> retNuke runRet ret Two Successive Applicatives
  • 73. 73 Hmm, Still Boilerplatey do (retPizza, retNuke) <- (,) <$> handlePizza <*> handleNuke let ret = valid <$> retPizza <*> retNuke runRet ret Combine Effectful
 IO Combine Effectful
 Ret
  • 74. 74 Compose Applicatives? data IO a = ... data Ret a = Ret { input :: a , effect :: IO ()} type Flow a = IO (Ret a) We need an Applicative instance for Flow
  • 75. 75 Applicatives Compose! Import Data.Functor.Compose type Compose f g a = Compose (f (g a)) type Flow a = Compose IO Ret a
  • 76. 76 Applicatives Compose! instance (Applicative f, Applicative g) => Applicative (Compose f g) where Compose f <*> Compose x = Compose (liftA2 (<*>) f x)
  • 77. 77 Running Compose runRet :: Ret Bool -> IO () runRet (Ret b e) = when b e runFlow :: Compose IO Ret Bool -> IO () runFlow (Compose e) = e >>= runRet
  • 78. 78 Defining Flows handlePizza :: Flow Boolean handlePizza = Compose $ do write "Do you want a pizza?" canOrder <- read return $ Ret canOrder $ when (canOrder == "Yes") orderPizza
  • 79. 79 Composing Flow With Business Logic valid <$> handlePizza <*> handleNukes <*> ...
  • 80. 80 No Boilerplate runFlow $ valid <$> handlePizza <*> handleNuke
  • 81. 81 ❑Type safety. Eliminates a large class of errors. ❑Effectful values are first class ❑Higher Order Patterns ❑Reduction in Boilerplate ❑Zero Cost Code Reuse Takeaways