Purely Functional I/O
Rúnar Bjarnason
@runarorama
QCon New York 2013

Thursday, June 13, 13
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/io-functional-side-effects

I...
Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the sprea...
Who is this jackass?
•
•
•

Thursday, June 13, 13

Enterprise Java refugee

Author, Functional Programming in Scala
mannin...
What you should take
away from this talk
•
•
•

Thursday, June 13, 13

I/O can be done with pure functional
programming.
I...
What is purely functional?

Thursday, June 13, 13
Functional programming is not
about lack of I/O

Thursday, June 13, 13
Functional programming is not
about first-class functions
or higher-order procedures.

Thursday, June 13, 13
Functional programming is not
about immutability.

Thursday, June 13, 13
Functional programming is
programming with pure functions

Thursday, June 13, 13
Functional programming is
programming with functions

Thursday, June 13, 13
A function of type (a → b) maps every value of type a
to exactly one value of type b.

(and nothing else)

Thursday, June ...
No side-effects.

Thursday, June 13, 13
Side-effects
•
•
•
•
•
Thursday, June 13, 13

Reading from a file
Writing to the console
Starting threads
Throwing excepti...
An expression e is referentially transparent if for all
programs p every occurrence of e in p can be replaced
with the res...
A function f is pure if f(x) is RT when x is RT.

Thursday, June 13, 13
A pure function always returns the same value
given the same input.

Thursday, June 13, 13
A pure function does not depend on anything
other than its argument.

Thursday, June 13, 13
The result of calling a pure function can be understood
completely by looking at the returned value.

Thursday, June 13, 1...
x = 2
y = 4
p = x + y

Thursday, June 13, 13
y = 4
p = 2 + y

Thursday, June 13, 13
p = 2 + 4

Thursday, June 13, 13
p = 6

Thursday, June 13, 13
def x = new Date().getTime
def y = 4
def p = x + y

Thursday, June 13, 13
def y = 4
def p = 1369250684475 + y

Thursday, June 13, 13
A side-effect is anything that violates referential
transparency.

Thursday, June 13, 13
Problems with sideeffecting I/O
•
•
•
•
•
Thursday, June 13, 13

Any function can perform I/O
Monolithic, non-modular, lim...
What we want
•
•
•
•

Thursday, June 13, 13

Clear separation of I/O concern
Modular and compositional API
Easy to test
Sc...
class Cafe {
def buyCoffee(cc: CreditCard): Coffee = {
val cup = new Coffee()
Payments.charge(cc, cup.price)
cup
}
}

Thur...
class Cafe {
def buyCoffee(cc: CreditCard, p: Payments): Coffee = {
val cup = new Coffee()
p.charge(cc, cup.price)
cup
}
}...
class Cafe {
def buyCoffee(cc: CreditCard): (Coffee, Charge) = {
val cup = new Coffee()
(cup, new Charge(cc, cup.price))
}...
First-class I/O
Instead of performing I/O as a side-effect,
return a value to the caller that describes an
interaction wit...
Java

class App {
public static void main(String[] args) {}
}

Thursday, June 13, 13
Java

class App {
public static IO main(String[] args) {}
}

Thursday, June 13, 13
Haskell

main :: IO ()

Thursday, June 13, 13
Haskell

getLine :: IO String
putStrLn :: String -> IO ()

Thursday, June 13, 13
Haskell

getInt :: IO Int
getInt = fmap read getLine
fmap :: (a -> b) -> IO a -> IO b
read :: String -> Int

Thursday, Jun...
Haskell

echo = getLine >>= putStrLn
(>>=) :: IO a -> (a -> IO b) -> IO b

Thursday, June 13, 13
Haskell

cat = forever echo
forever x = x >> forever x
(>>) :: IO a -> IO b -> IO b

Thursday, June 13, 13
Haskell

cat = forever do
x <- getLine
putStrLn x
forever x = do { x; forever x }

Thursday, June 13, 13
Scala

println("What is your name?")
println("Hello, " + readLine)

Thursday, June 13, 13
Haskell

do
putStrLn "What is your name?"
putStrLn ("Hello, " ++ getLine)

Thursday, June 13, 13
Haskell
do

putStrLn "What is your name?"
putStrLn ("Hello, " ++ getLine)

Couldn't match expected type `String' with
actu...
Haskell

putStrLn "What is your name?" >>
getLine >>= greet
greet name = putStrLn ("Hello, " ++ name)

Thursday, June 13, ...
Haskell

do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Hello, " ++ name)

Thursday, June 13, 13
In Haskell, every program is
a single referentially transparent expression.

Thursday, June 13, 13
Benefits of IO
datatype
•
•
•

Thursday, June 13, 13

No side-effects. An IO action is a RT
description.
Effects are exter...
Library of I/O
functions
sequence
mapM
replicateM
liftM2
forever
while

::
::
::
::
::
::

[IO a] -> IO [a]
(a -> IO b) ->...
Novel compositions

getTenLines :: IO [String]
getTenLines = sequence (replicate 10 getLine)

Thursday, June 13, 13
The world-as-state
model
class IO[A](val apply: RealWorld => (A, RealWorld))

Thursday, June 13, 13
The world-as-state
model
class IO[A](val apply: () => A)

Thursday, June 13, 13
The end of the world
sealed trait RealWorld
abstract class Program {
private val realWorld = new RealWorld {}
final def ma...
Example IO actions
def io[A](a: => A): IO[A] =
new IO(() => a)
def putStrLn(s: String): IO[Unit] =
io(println(s))
def getL...
Composing actions

main = getLine >>= putStrLn >> main

Thursday, June 13, 13
Composing actions
class IO[A](val apply: () => A) {
def >>=[B](f: A => IO[B]): IO[B] = new IO[B](() => {
apply(rw)
f(a).ap...
Composing actions
new Program {
def pureMain(args: IndexedSeq[String]): IO[Unit] =
(getLine >>= putStrLn) >> pureMain(args...
Composing actions
main = do
x <- getLine
putStrLn x
main

Thursday, June 13, 13
Composing actions
new Program {
def pureMain(args: IndexedSeq[String]): IO[Unit] =
for {
line <- getLine
_ <- putStrLn(lin...
Composing actions
class IO[A](val apply: RealWorld => (A, RealWorld)) {
def >>=[B](f: A => IO[B]): IO[B] = new IO[B] { rw ...
Issues with
world-as-state
An IO[A] could do anything at all.

Thursday, June 13, 13
Issues with
world-as-state
This implementation overflows the stack in Scala.
Solved with trampolining.

Thursday, June 13,...
Issues with
world-as-state
main = forever (getLine >>= putStrLn)
main = main

Thursday, June 13, 13
The “free monad”
model
sealed trait IO[A]
case class Pure[A](a: A) extends IO[A]
case class Request[I,A](
req: External[I]...
The “free monad”
model
sealed trait IO[F[_],A]
case class Pure[F[_],A](a: A) extends IO[F,A]
case class Request[F[_],I,A](...
Any effect
trait Runnable[A] { def run: A }
def delay[A](a: => A) =
new Runnable[A] {
def run = a
}
type AnyIO[A] = IO[Run...
Console I/O
trait Console[A]
case object ReadLine extends Console[Option[String]]
case class PrintLn(s: String) extends Co...
The end of the world
trait Run[F[_]] {
def apply[A](expr: F[A]): (A, Run[F])
}
abstract class App[F[_]](R: Run[F]) {
def m...
Console I/O
object RunConsole extends Run[Console] {
def apply[A](c: Console[A]) = c match {
case ReadLine => (Option(read...
We can use this same model for

•
•
•
•
•
•

Thursday, June 13, 13

file system access
network sockets
HTTP requests
JDBC ...
Summary
•
•
•

It really is purely functional.

•
Thursday, June 13, 13

I/O can be done with pure functional
programming....
Afterword
•
•
•
•
Thursday, June 13, 13

There’s a lot more to be said
This presentation is simplified
Streaming I/O
Mutab...
Chapter 13
Functional Programming
in Scala
manning.com/bjarnason

Thursday, June 13, 13
Questions?

Thursday, June 13, 13
Watch the video with slide synchronization on
InfoQ.com!
http://www.infoq.com/presentations/iofunctional-side-effects
Upcoming SlideShare
Loading in...5
×

Purely Functional I/O

920

Published on

Video and slides synchronized, mp3 and slide download available at URL http://bit.ly/1gZiqcN.

Runar Bjarnason explains how to approach I/O from a purely functional perspective, exploring the space of existing solutions, their benefits, and drawbacks.Filmed at qconnewyork.com.

Runar Bjarnason is an independent software developer, co-author of "Functional Programming in Scala" (Manning, 2013), and a functional programming extremist.

Published in: Technology, Education
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
920
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Transcript of "Purely Functional I/O"

  1. 1. Purely Functional I/O Rúnar Bjarnason @runarorama QCon New York 2013 Thursday, June 13, 13
  2. 2. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /io-functional-side-effects InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month
  3. 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  4. 4. Who is this jackass? • • • Thursday, June 13, 13 Enterprise Java refugee Author, Functional Programming in Scala manning.com/bjarnason Partially responsible for Scalaz and Functional Java
  5. 5. What you should take away from this talk • • • Thursday, June 13, 13 I/O can be done with pure functional programming. It really is purely functional. How it’s done and why it’s done that way.
  6. 6. What is purely functional? Thursday, June 13, 13
  7. 7. Functional programming is not about lack of I/O Thursday, June 13, 13
  8. 8. Functional programming is not about first-class functions or higher-order procedures. Thursday, June 13, 13
  9. 9. Functional programming is not about immutability. Thursday, June 13, 13
  10. 10. Functional programming is programming with pure functions Thursday, June 13, 13
  11. 11. Functional programming is programming with functions Thursday, June 13, 13
  12. 12. A function of type (a → b) maps every value of type a to exactly one value of type b. (and nothing else) Thursday, June 13, 13
  13. 13. No side-effects. Thursday, June 13, 13
  14. 14. Side-effects • • • • • Thursday, June 13, 13 Reading from a file Writing to the console Starting threads Throwing exceptions Mutating memory
  15. 15. An expression e is referentially transparent if for all programs p every occurrence of e in p can be replaced with the result of evaluating e without changing the meaning of p. Thursday, June 13, 13
  16. 16. A function f is pure if f(x) is RT when x is RT. Thursday, June 13, 13
  17. 17. A pure function always returns the same value given the same input. Thursday, June 13, 13
  18. 18. A pure function does not depend on anything other than its argument. Thursday, June 13, 13
  19. 19. The result of calling a pure function can be understood completely by looking at the returned value. Thursday, June 13, 13
  20. 20. x = 2 y = 4 p = x + y Thursday, June 13, 13
  21. 21. y = 4 p = 2 + y Thursday, June 13, 13
  22. 22. p = 2 + 4 Thursday, June 13, 13
  23. 23. p = 6 Thursday, June 13, 13
  24. 24. def x = new Date().getTime def y = 4 def p = x + y Thursday, June 13, 13
  25. 25. def y = 4 def p = 1369250684475 + y Thursday, June 13, 13
  26. 26. A side-effect is anything that violates referential transparency. Thursday, June 13, 13
  27. 27. Problems with sideeffecting I/O • • • • • Thursday, June 13, 13 Any function can perform I/O Monolithic, non-modular, limited reuse Novel compositions difficult Difficult to test Difficult to scale
  28. 28. What we want • • • • Thursday, June 13, 13 Clear separation of I/O concern Modular and compositional API Easy to test Scales well
  29. 29. class Cafe { def buyCoffee(cc: CreditCard): Coffee = { val cup = new Coffee() Payments.charge(cc, cup.price) cup } } Thursday, June 13, 13
  30. 30. class Cafe { def buyCoffee(cc: CreditCard, p: Payments): Coffee = { val cup = new Coffee() p.charge(cc, cup.price) cup } } Thursday, June 13, 13
  31. 31. class Cafe { def buyCoffee(cc: CreditCard): (Coffee, Charge) = { val cup = new Coffee() (cup, new Charge(cc, cup.price)) } } Thursday, June 13, 13
  32. 32. First-class I/O Instead of performing I/O as a side-effect, return a value to the caller that describes an interaction with the external system. Thursday, June 13, 13
  33. 33. Java class App { public static void main(String[] args) {} } Thursday, June 13, 13
  34. 34. Java class App { public static IO main(String[] args) {} } Thursday, June 13, 13
  35. 35. Haskell main :: IO () Thursday, June 13, 13
  36. 36. Haskell getLine :: IO String putStrLn :: String -> IO () Thursday, June 13, 13
  37. 37. Haskell getInt :: IO Int getInt = fmap read getLine fmap :: (a -> b) -> IO a -> IO b read :: String -> Int Thursday, June 13, 13
  38. 38. Haskell echo = getLine >>= putStrLn (>>=) :: IO a -> (a -> IO b) -> IO b Thursday, June 13, 13
  39. 39. Haskell cat = forever echo forever x = x >> forever x (>>) :: IO a -> IO b -> IO b Thursday, June 13, 13
  40. 40. Haskell cat = forever do x <- getLine putStrLn x forever x = do { x; forever x } Thursday, June 13, 13
  41. 41. Scala println("What is your name?") println("Hello, " + readLine) Thursday, June 13, 13
  42. 42. Haskell do putStrLn "What is your name?" putStrLn ("Hello, " ++ getLine) Thursday, June 13, 13
  43. 43. Haskell do putStrLn "What is your name?" putStrLn ("Hello, " ++ getLine) Couldn't match expected type `String' with actual type `IO String' In the second argument of `(++)', namely `getLine' Thursday, June 13, 13
  44. 44. Haskell putStrLn "What is your name?" >> getLine >>= greet greet name = putStrLn ("Hello, " ++ name) Thursday, June 13, 13
  45. 45. Haskell do putStrLn "What is your name?" name <- getLine putStrLn ("Hello, " ++ name) Thursday, June 13, 13
  46. 46. In Haskell, every program is a single referentially transparent expression. Thursday, June 13, 13
  47. 47. Benefits of IO datatype • • • Thursday, June 13, 13 No side-effects. An IO action is a RT description. Effects are external to our program. Action-manipulating functions!
  48. 48. Library of I/O functions sequence mapM replicateM liftM2 forever while :: :: :: :: :: :: [IO a] -> IO [a] (a -> IO b) -> [a] -> IO [b] Int -> IO a -> IO [a] (a -> b -> c) -> IO a -> IO b -> IO c IO a -> IO b IO Bool -> IO () And many more Thursday, June 13, 13
  49. 49. Novel compositions getTenLines :: IO [String] getTenLines = sequence (replicate 10 getLine) Thursday, June 13, 13
  50. 50. The world-as-state model class IO[A](val apply: RealWorld => (A, RealWorld)) Thursday, June 13, 13
  51. 51. The world-as-state model class IO[A](val apply: () => A) Thursday, June 13, 13
  52. 52. The end of the world sealed trait RealWorld abstract class Program { private val realWorld = new RealWorld {} final def main(args: Array[String]): Unit = pureMain(args).apply() def pureMain(args: IndexedSeq[String]): IO[Unit] } Thursday, June 13, 13
  53. 53. Example IO actions def io[A](a: => A): IO[A] = new IO(() => a) def putStrLn(s: String): IO[Unit] = io(println(s)) def getLine: IO[String] = io(readLine) Thursday, June 13, 13
  54. 54. Composing actions main = getLine >>= putStrLn >> main Thursday, June 13, 13
  55. 55. Composing actions class IO[A](val apply: () => A) { def >>=[B](f: A => IO[B]): IO[B] = new IO[B](() => { apply(rw) f(a).apply(rw2) }) def >>[B](b: => IO[B]): IO[B] = >>=(_ => b) } Thursday, June 13, 13
  56. 56. Composing actions new Program { def pureMain(args: IndexedSeq[String]): IO[Unit] = (getLine >>= putStrLn) >> pureMain(args) } Thursday, June 13, 13
  57. 57. Composing actions main = do x <- getLine putStrLn x main Thursday, June 13, 13
  58. 58. Composing actions new Program { def pureMain(args: IndexedSeq[String]): IO[Unit] = for { line <- getLine _ <- putStrLn(line) _ <- pureMain(args) } yield () } Thursday, June 13, 13
  59. 59. Composing actions class IO[A](val apply: RealWorld => (A, RealWorld)) { def >>=[B](f: A => IO[B]): IO[B] = new IO[B] { rw => val (a, rw2) = apply(rw) f(a) } def >>[B](b: => IO[B]): IO[B] = >>=(_ => b) } Thursday, June 13, 13 def flatMap[B](f: A => IO[B]): IO[B] = >>=(f) def map[B](f: A => B): IO[B] = >>=(a => io(f(a)))
  60. 60. Issues with world-as-state An IO[A] could do anything at all. Thursday, June 13, 13
  61. 61. Issues with world-as-state This implementation overflows the stack in Scala. Solved with trampolining. Thursday, June 13, 13
  62. 62. Issues with world-as-state main = forever (getLine >>= putStrLn) main = main Thursday, June 13, 13
  63. 63. The “free monad” model sealed trait IO[A] case class Pure[A](a: A) extends IO[A] case class Request[I,A]( req: External[I], k: I => IO[A]) extends IO[A] Thursday, June 13, 13
  64. 64. The “free monad” model sealed trait IO[F[_],A] case class Pure[F[_],A](a: A) extends IO[F,A] case class Request[F[_],I,A]( req: F[I], k: I => IO[A]) extends IO[A] Thursday, June 13, 13
  65. 65. Any effect trait Runnable[A] { def run: A } def delay[A](a: => A) = new Runnable[A] { def run = a } type AnyIO[A] = IO[Runnable,A] Thursday, June 13, 13
  66. 66. Console I/O trait Console[A] case object ReadLine extends Console[Option[String]] case class PrintLn(s: String) extends Console[Unit] type ConsoleIO[A] = IO[Console,A] Thursday, June 13, 13
  67. 67. The end of the world trait Run[F[_]] { def apply[A](expr: F[A]): (A, Run[F]) } abstract class App[F[_]](R: Run[F]) { def main(a: Array[String]): IO[F,Unit] = run(pureMain(a)) def pureMain(a: Array[String]): IO[F,Unit] } Thursday, June 13, 13 def run[A](io: IO[F,A]): A = io match { case Pure(a) => a case Request(req, k) => val (e, r2) = R(expr) run(r2)(k(e)) }
  68. 68. Console I/O object RunConsole extends Run[Console] { def apply[A](c: Console[A]) = c match { case ReadLine => (Option(readLine), RunConsole) case PrintLn(s) => (println(s), RunConsole) } } IO.run(RunConsole): IO[Console,A] => A Thursday, June 13, 13
  69. 69. We can use this same model for • • • • • • Thursday, June 13, 13 file system access network sockets HTTP requests JDBC actions system clock access non-blocking I/O
  70. 70. Summary • • • It really is purely functional. • Thursday, June 13, 13 I/O can be done with pure functional programming. Now you know how it’s done. You don’t need a purely functional language to do it.
  71. 71. Afterword • • • • Thursday, June 13, 13 There’s a lot more to be said This presentation is simplified Streaming I/O Mutable arrays and references
  72. 72. Chapter 13 Functional Programming in Scala manning.com/bjarnason Thursday, June 13, 13
  73. 73. Questions? Thursday, June 13, 13
  74. 74. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations/iofunctional-side-effects

×