• Save
Purely Functional I/O
Upcoming SlideShare
Loading in...5
×
 

Purely Functional I/O

on

  • 856 views

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

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.

Statistics

Views

Total Views
856
Views on SlideShare
856
Embed Views
0

Actions

Likes
0
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Purely Functional I/O Purely Functional I/O Presentation Transcript

  • 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 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
  • 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
  • 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
  • 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.
  • 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 13, 13
  • No side-effects. Thursday, June 13, 13
  • Side-effects • • • • • Thursday, June 13, 13 Reading from a file Writing to the console Starting threads Throwing exceptions Mutating memory
  • 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
  • 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, 13
  • 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, limited reuse Novel compositions difficult Difficult to test Difficult to scale
  • What we want • • • • Thursday, June 13, 13 Clear separation of I/O concern Modular and compositional API Easy to test Scales well
  • class Cafe { def buyCoffee(cc: CreditCard): Coffee = { val cup = new Coffee() Payments.charge(cc, cup.price) cup } } Thursday, June 13, 13
  • class Cafe { def buyCoffee(cc: CreditCard, p: Payments): Coffee = { val cup = new Coffee() p.charge(cc, cup.price) cup } } Thursday, June 13, 13
  • class Cafe { def buyCoffee(cc: CreditCard): (Coffee, Charge) = { val cup = new Coffee() (cup, new Charge(cc, cup.price)) } } Thursday, June 13, 13
  • 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
  • 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, June 13, 13
  • 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 actual type `IO String' In the second argument of `(++)', namely `getLine' Thursday, June 13, 13
  • Haskell putStrLn "What is your name?" >> getLine >>= greet greet name = putStrLn ("Hello, " ++ name) Thursday, June 13, 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 external to our program. Action-manipulating functions!
  • 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
  • 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 main(args: Array[String]): Unit = pureMain(args).apply() def pureMain(args: IndexedSeq[String]): IO[Unit] } Thursday, June 13, 13
  • 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
  • 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).apply(rw2) }) def >>[B](b: => IO[B]): IO[B] = >>=(_ => b) } Thursday, June 13, 13
  • Composing actions new Program { def pureMain(args: IndexedSeq[String]): IO[Unit] = (getLine >>= putStrLn) >> pureMain(args) } Thursday, June 13, 13
  • 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(line) _ <- pureMain(args) } yield () } Thursday, June 13, 13
  • 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)))
  • 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, 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], k: I => IO[A]) extends IO[A] Thursday, June 13, 13
  • 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
  • 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
  • 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
  • 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)) }
  • 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
  • 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
  • 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.
  • Afterword • • • • Thursday, June 13, 13 There’s a lot more to be said This presentation is simplified Streaming I/O Mutable arrays and references
  • 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