SlideShare une entreprise Scribd logo
1  sur  84
Télécharger pour lire hors ligne
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?

Contenu connexe

Plus de Tech Triveni

Semi-Supervised Insight Generation from Petabyte Scale Text Data
Semi-Supervised Insight Generation from Petabyte Scale Text DataSemi-Supervised Insight Generation from Petabyte Scale Text Data
Semi-Supervised Insight Generation from Petabyte Scale Text Data
Tech Triveni
 
Proximity Targeting at Scale using Big Data Platforms
Proximity Targeting at Scale using Big Data PlatformsProximity Targeting at Scale using Big Data Platforms
Proximity Targeting at Scale using Big Data Platforms
Tech Triveni
 
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Tech Triveni
 

Plus de Tech Triveni (15)

Semi-Supervised Insight Generation from Petabyte Scale Text Data
Semi-Supervised Insight Generation from Petabyte Scale Text DataSemi-Supervised Insight Generation from Petabyte Scale Text Data
Semi-Supervised Insight Generation from Petabyte Scale Text Data
 
Finding the best solution for Image Processing
Finding the best solution for Image ProcessingFinding the best solution for Image Processing
Finding the best solution for Image Processing
 
Proximity Targeting at Scale using Big Data Platforms
Proximity Targeting at Scale using Big Data PlatformsProximity Targeting at Scale using Big Data Platforms
Proximity Targeting at Scale using Big Data Platforms
 
Effecting Pure Change - How anything ever gets done in functional programming...
Effecting Pure Change - How anything ever gets done in functional programming...Effecting Pure Change - How anything ever gets done in functional programming...
Effecting Pure Change - How anything ever gets done in functional programming...
 
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
Becoming a Functional Programmer - Harit Himanshu (Nomis Solutions)
 
Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...
Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...
Live coding session on AI / ML using Google Tensorflow (Python) - Tanmoy Deb ...
 
Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)
Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)
Distributing the SMACK stack - Kubernetes VS DCOS - Sahil Sawhney (Knoldus Inc.)
 
Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...
Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...
Blue Pill / Red Pill : The Matrix of thousands of data streams - Himanshu Gup...
 
UX in Big Data Analytics - Paramjit Jolly (Guavus)
UX in Big Data Analytics - Paramjit Jolly (Guavus)UX in Big Data Analytics - Paramjit Jolly (Guavus)
UX in Big Data Analytics - Paramjit Jolly (Guavus)
 
Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)
Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)
Simplified Scala Monads And Transformation - Harmeet Singh (Knoldus Inc.)
 
Micro Frontends Architecture - Jitendra kumawat (Guavus)
Micro Frontends Architecture - Jitendra kumawat (Guavus)Micro Frontends Architecture - Jitendra kumawat (Guavus)
Micro Frontends Architecture - Jitendra kumawat (Guavus)
 
Apache CarbonData+Spark to realize data convergence and Unified high performa...
Apache CarbonData+Spark to realize data convergence and Unified high performa...Apache CarbonData+Spark to realize data convergence and Unified high performa...
Apache CarbonData+Spark to realize data convergence and Unified high performa...
 
Micro Frontends Architecture - Jitendra kumawat (Guavus)
Micro Frontends Architecture - Jitendra kumawat (Guavus)Micro Frontends Architecture - Jitendra kumawat (Guavus)
Micro Frontends Architecture - Jitendra kumawat (Guavus)
 
Augmentation of bids for programmatic ad auctions @ real time with the power ...
Augmentation of bids for programmatic ad auctions @ real time with the power ...Augmentation of bids for programmatic ad auctions @ real time with the power ...
Augmentation of bids for programmatic ad auctions @ real time with the power ...
 
Programmable Decision Tree @Scale for Programmatic Media Buying - Rohit Sriva...
Programmable Decision Tree @Scale for Programmatic Media Buying - Rohit Sriva...Programmable Decision Tree @Scale for Programmatic Media Buying - Rohit Sriva...
Programmable Decision Tree @Scale for Programmatic Media Buying - Rohit Sriva...
 

Dernier

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Dernier (20)

Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 

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