Skip to content

Commit e727b77

Browse files
committed
Added IOFunctions.interactWhile and some sequence and traverse methods for Stream to support interactive IO from stdin to stdout
1 parent b0318d3 commit e727b77

3 files changed

Lines changed: 90 additions & 3 deletions

File tree

core/src/main/java/fj/data/IOFunctions.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ public A run() throws IOException {
172172
};
173173
}
174174

175+
public static final IO<Unit> ioUnit = unit(Unit.unit());
176+
175177
public static final <A> IO<A> lazy(final F0<A> p) {
176178
return fromF(p);
177179
}
@@ -404,6 +406,18 @@ public static <A, B> IO<B> flatMap(final IO<A> io, final F<A, IO<B>> f) {
404406
return bind(io, f);
405407
}
406408

409+
/**
410+
* Read lines from stdin until condition is not met, transforming each line and printing
411+
* the result to stdout.
412+
* @param condition Read lines until a line does not satisfy condition
413+
* @param transform Function to change line value
414+
*/
415+
public static IO<Unit> interactWhile(F<String, Boolean> condition, F<String, String> transform) {
416+
Stream<IO<String>> s1 = Stream.repeat(IOFunctions.stdinReadLine());
417+
IO<Stream<String>> io = sequenceWhile(s1, condition);
418+
return () -> runSafe(io).foreach(s -> runSafe(stdoutPrintln(transform.f(s))));
419+
}
420+
407421
public static <A> IO<Stream<A>> sequenceWhileEager(final Stream<IO<A>> stream, final F<A, Boolean> f) {
408422
return new IO<Stream<A>>() {
409423
@Override

core/src/main/java/fj/data/Stream.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public final Stream<A> removeAll(final F<A, Boolean> f) {
365365
*/
366366
public static <A, B> F<B, Stream<A>> sequence_(final Stream<F<B, A>> fs) {
367367
return fs.foldRight((baf, p1) -> Function.bind(baf, p1._1(), Function.curry((a, stream) -> cons(a, p(stream)))), Function
368-
.<B, Stream<A>>constant(Stream.<A>nil()));
368+
.<B, Stream<A>>constant(Stream.<A>nil()));
369369
}
370370

371371
/**
@@ -527,6 +527,36 @@ public final <B> Stream<B> sequence(final Stream<B> bs) {
527527
return bind(c);
528528
}
529529

530+
/**
531+
* Sequence through the Stream monad.
532+
*
533+
* @param io The IO stream to sequence.
534+
* @return The stream of IOs after sequencing.
535+
*/
536+
public static <A> Stream<IO<A>> sequence(final IO<Stream<A>> io) {
537+
return IOFunctions.runSafe(io).map(a -> IOFunctions.unit(a));
538+
}
539+
540+
/**
541+
* Sequence through the Stream monad.
542+
*
543+
* @param p The lazy stream to sequence.
544+
* @return The stream of (pre-calculated) lazy values after sequencing.
545+
*/
546+
public static <A> Stream<P1<A>> sequence(final P1<Stream<A>> p) {
547+
return p._1().map(a -> P.p(a));
548+
}
549+
550+
/**
551+
* Sequence through the Stream monad.
552+
*
553+
* @param o The optional stream to sequence.
554+
* @return The stream of options after sequencing.
555+
*/
556+
public static <A> Stream<Option<A>> sequence(final Option<Stream<A>> o) {
557+
return o.isNone() ? Stream.nil() : o.some().map(a -> Option.some(a));
558+
}
559+
530560
/**
531561
* Performs function application within a stream (applicative functor pattern).
532562
*
@@ -1082,6 +1112,28 @@ public Stream<A> _1() {
10821112
Stream.<A>nil();
10831113
}
10841114

1115+
/**
1116+
* Traversable instance of Stream for IO.
1117+
*
1118+
* @return traversed value
1119+
*/
1120+
public final <B> IO<Stream<B>> traverseIO(F<A, IO<B>> f) {
1121+
return this.foldRight1((a, acc) ->
1122+
IOFunctions.bind(acc, (Stream<B> bs) ->
1123+
IOFunctions.map(f.f(a), b ->
1124+
bs.cons(b))), IOFunctions.unit(Stream.<B>nil()));
1125+
1126+
}
1127+
1128+
/**
1129+
* Traversable instance of Stream for Option.
1130+
*
1131+
* @return traversed value
1132+
*/
1133+
public final <B> Option<Stream<B>> traverseOption(F<A, Option<B>> f) {
1134+
return this.foldRight1((a, acc) -> acc.bind(bs -> f.f(a).map(b -> bs.cons(b))), some(Stream.<B>nil()));
1135+
}
1136+
10851137
/**
10861138
* Removes elements from the head of this stream that do not match the given predicate function
10871139
* until an element is found that does match or the stream is exhausted.

demo/src/main/java/fj/demo/realworld/Chapter7.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fj.demo.realworld;
22

3+
import fj.data.IO;
4+
import fj.data.IOFunctions;
35
import fj.data.LazyString;
46
import fj.data.Stream;
57

@@ -13,17 +15,36 @@
1315
public class Chapter7 {
1416

1517
public static void main(String[] args) {
16-
interactToUpper();
18+
// toUpperLazy();
19+
// toUpperByLine();
20+
toUpperInteract();
1721
}
1822

1923
/**
2024
* Lazy interact to upper, shows the first lazy string line.
2125
*/
22-
public static void interactToUpper() {
26+
public static void toUpperLazy() {
2327
runSafe(interact(ls -> {
2428
Stream<String> stream = ls.lines().map((LazyString ls2) -> ls2.eval().toUpperCase());
2529
return LazyString.unlines(stream.map(s -> LazyString.str(s)));
2630
}));
2731
}
2832

33+
/**
34+
* Read each line, convert to uppercase and print on stdout, until an empty line
35+
*/
36+
public static void toUpperByLine() {
37+
Stream<IO<String>> s1 = Stream.repeat(IOFunctions.stdinReadLine());
38+
IO<Stream<String>> io = sequenceWhile(s1, s -> s.trim().length() > 0);
39+
runSafe(io).foreachDoEffect(s -> runSafe(stdoutPrintln(s.toUpperCase())));
40+
}
41+
42+
/**
43+
* Read from stdin each line, whilst each line is not empty, print
44+
* uppercase line to stdout
45+
*/
46+
public static void toUpperInteract() {
47+
runSafe(interactWhile(s -> s.trim().length() > 0, s -> s.toUpperCase()));
48+
}
49+
2950
}

0 commit comments

Comments
 (0)