|
| 1 | +package fj.demo; |
| 2 | + |
| 3 | +import fj.F; |
| 4 | +import fj.F1Functions; |
| 5 | +import fj.F1W; |
| 6 | +import fj.Unit; |
| 7 | +import fj.data.IO; |
| 8 | +import fj.data.IOFunctions; |
| 9 | +import fj.data.IOW; |
| 10 | + |
| 11 | +import java.io.BufferedReader; |
| 12 | +import java.io.IOException; |
| 13 | +import java.io.InputStreamReader; |
| 14 | + |
| 15 | +import static fj.data.IOFunctions.stdinReadLine; |
| 16 | +import static fj.data.IOFunctions.stdoutPrint; |
| 17 | +import static fj.data.IOFunctions.stdoutPrintln; |
| 18 | +import static fj.data.IOFunctions.toSafeValidation; |
| 19 | + |
| 20 | +/** |
| 21 | + * Demonstrates how to use <code>IO</code> and basic function composition. |
| 22 | + */ |
| 23 | +public class IOWalkthrough { |
| 24 | + public static void main(String[] args) { |
| 25 | + |
| 26 | + // IO is just a container to defer a computation (lazy), with the intention to encapsulate computations that either |
| 27 | + // consume and/or produce side-effects |
| 28 | + // the computation is not (yet) executed on creation hence it can be treated like a value |
| 29 | + |
| 30 | + final IO<Unit> askName = () -> { |
| 31 | + System.out.println("Hi, what's your name?"); |
| 32 | + return Unit.unit(); |
| 33 | + }; |
| 34 | + |
| 35 | + // fj.data.IOFunctions contains a lot of convenience functions regarding IO, the above example could be rewritten with IOFunctions.stdoutPrintln |
| 36 | + // we now create an IO value to prompt for the name if executed |
| 37 | + |
| 38 | + IO<Unit> promptName = IOFunctions.stdoutPrint("Name: "); |
| 39 | + |
| 40 | + // we can compose these two values with fj.data.IOFunctions.append, since they both are not interested in any runtime value |
| 41 | + |
| 42 | + IO<Unit> askAndPromptName = IOFunctions.append(askName, promptName); |
| 43 | + |
| 44 | + // now we create an IO value to read a line from stdin |
| 45 | + |
| 46 | + final IO<String> readName = () -> new BufferedReader(new InputStreamReader(System.in)).readLine(); |
| 47 | + |
| 48 | + // this is the same as IOFunctions.stdinReadLine() |
| 49 | + |
| 50 | + // now we create a function which takes a string, upper cases it and creates an IO value that would print the upper cased string if executed |
| 51 | + |
| 52 | + final F<String, IO<Unit>> upperCaseAndPrint = F1Functions.<String, IO<Unit>, String>o(IOFunctions::stdoutPrintln).f(s -> s.toUpperCase()); |
| 53 | + |
| 54 | + // we now want to compose reading the name with printing it, for that we need to have access to the runtime value that is returned when the |
| 55 | + // IO value for read is executed, hence we use fj.data.IOFunctions.bind instead of fj.data.IOFunctions.append |
| 56 | + |
| 57 | + final IO<Unit> readAndPrintUpperCasedName = IOFunctions.bind(readName, upperCaseAndPrint); |
| 58 | + |
| 59 | + // so append is really just a specialised form of bind, ignoring the runtime value of the IO execution that was composed before us |
| 60 | + |
| 61 | + final IO<Unit> program = IOFunctions.bind(askAndPromptName, ignored -> readAndPrintUpperCasedName); |
| 62 | + |
| 63 | + // this is the same as writing IOFunctions.append(askAndPromptName, readAndPrintUpperCasedName) |
| 64 | + |
| 65 | + // we have recorded the entire program, but have not run anything yet |
| 66 | + // now we get to the small dirty part at the end of our program where we actually execute it |
| 67 | + // we can either choose to just call program.run(), which allows the execution to escape |
| 68 | + // or we use safe to receive an fj.data.Either with the potential exception on the left side |
| 69 | + |
| 70 | + toSafeValidation(program).run().on((IOException e) -> { e.printStackTrace(); return Unit.unit(); }); |
| 71 | + |
| 72 | + // doing function composition like this can be quite cumbersome, since you will end up nesting parenthesis unless you flatten it out by |
| 73 | + // assigning the functions to variables like above, but you can use the fj.F1W syntax wrapper for composing single-argument functions and fj.data.IOW |
| 74 | + // for composing IO values instead, the entire program can be written like so: |
| 75 | + |
| 76 | + IOW.lift(stdoutPrintln("What's your name again?")) |
| 77 | + .append(stdoutPrint("Name: ")) |
| 78 | + .append(stdinReadLine()) |
| 79 | + .bind(F1W.lift((String s) -> s.toUpperCase()).andThen(IOFunctions::stdoutPrintln)) |
| 80 | + .safe().run().on((IOException e) -> { e.printStackTrace(); return Unit.unit(); }); |
| 81 | + } |
| 82 | +} |
| 83 | + |
0 commit comments