comparison src/main/java/fj/data/IOFunctions.java @ 0:fe80c1edf1be

add getLoop
author tatsuki
date Fri, 20 Mar 2015 21:04:03 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:fe80c1edf1be
1 package fj.data;
2
3 import static fj.Bottom.errorF;
4 import static fj.Function.constant;
5 import static fj.Function.partialApply2;
6
7 import java.io.BufferedReader;
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.IOException;
11 import java.io.InputStreamReader;
12 import java.io.Reader;
13 import java.nio.charset.Charset;
14 import java.util.Arrays;
15
16 import fj.*;
17 import fj.data.Iteratee.Input;
18 import fj.data.Iteratee.IterV;
19 import fj.function.Try0;
20
21 /**
22 * IO monad for processing files, with main methods {@link #enumFileLines },
23 * {@link #enumFileChars} and {@link #enumFileCharChunks}
24 * (the latter one is the fastest as char chunks read from the file are directly passed to the iteratee
25 * without indirection in between).
26 *
27 * @author Martin Grotzke
28 *
29 */
30 public class IOFunctions {
31
32 private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
33
34 public static <A> Try0<A, IOException> toTry(IO<A> io) {
35 return () -> io.run();
36 }
37
38 public static <A> P1<Validation<IOException, A>> p(IO<A> io) {
39 return Try.f(toTry(io));
40 }
41
42 public static <A> IO<A> io(P1<A> p) {
43 return () -> p._1();
44 }
45
46 public static <A> IO<A> io(Try0<A, ? extends IOException> t) {
47 return () -> t.f();
48 }
49
50 public static final F<Reader, IO<Unit>> closeReader =
51 new F<Reader, IO<Unit>>() {
52 @Override
53 public IO<Unit> f(final Reader r) {
54 return closeReader(r);
55 }
56 };
57
58 public static IO<Unit> closeReader(final Reader r) {
59 return new IO<Unit>() {
60 @Override
61 public Unit run() throws IOException {
62 r.close();
63 return Unit.unit();
64 }
65 };
66 }
67
68 /**
69 * An IO monad that reads lines from the given file (using a {@link BufferedReader}) and passes
70 * lines to the provided iteratee. May not be suitable for files with very long
71 * lines, consider to use {@link #enumFileCharChunks} or {@link #enumFileChars}
72 * as an alternative.
73 *
74 * @param f the file to read, must not be <code>null</code>
75 * @param encoding the encoding to use, {@link Option#none()} means platform default
76 * @param i the iteratee that is fed with lines read from the file
77 */
78 public static <A> IO<IterV<String, A>> enumFileLines(final File f, final Option<Charset> encoding, final IterV<String, A> i) {
79 return bracket(bufferedReader(f, encoding)
80 , Function.<BufferedReader, IO<Unit>>vary(closeReader)
81 , partialApply2(IOFunctions.<A>lineReader(), i));
82 }
83
84 /**
85 * An IO monad that reads char chunks from the given file and passes them to the given iteratee.
86 *
87 * @param f the file to read, must not be <code>null</code>
88 * @param encoding the encoding to use, {@link Option#none()} means platform default
89 * @param i the iteratee that is fed with char chunks read from the file
90 */
91 public static <A> IO<IterV<char[], A>> enumFileCharChunks(final File f, final Option<Charset> encoding, final IterV<char[], A> i) {
92 return bracket(fileReader(f, encoding)
93 , Function.<Reader, IO<Unit>>vary(closeReader)
94 , partialApply2(IOFunctions.<A>charChunkReader(), i));
95 }
96
97 /**
98 * An IO monad that reads char chunks from the given file and passes single chars to the given iteratee.
99 *
100 * @param f the file to read, must not be <code>null</code>
101 * @param encoding the encoding to use, {@link Option#none()} means platform default
102 * @param i the iteratee that is fed with chars read from the file
103 */
104 public static <A> IO<IterV<Character, A>> enumFileChars(final File f, final Option<Charset> encoding, final IterV<Character, A> i) {
105 return bracket(fileReader(f, encoding)
106 , Function.<Reader, IO<Unit>>vary(closeReader)
107 , partialApply2(IOFunctions.<A>charChunkReader2(), i));
108 }
109
110 public static IO<BufferedReader> bufferedReader(final File f, final Option<Charset> encoding) {
111 return IOFunctions.map(fileReader(f, encoding), new F<Reader, BufferedReader>() {
112 @Override
113 public BufferedReader f(final Reader a) {
114 return new BufferedReader(a);
115 }});
116 }
117
118 public static IO<Reader> fileReader(final File f, final Option<Charset> encoding) {
119 return new IO<Reader>() {
120 @Override
121 public Reader run() throws IOException {
122 final FileInputStream fis = new FileInputStream(f);
123 return encoding.isNone() ? new InputStreamReader(fis) : new InputStreamReader(fis, encoding.some());
124 }
125 };
126 }
127
128 public static final <A, B, C> IO<C> bracket(final IO<A> init, final F<A, IO<B>> fin, final F<A, IO<C>> body) {
129 return new IO<C>() {
130 @Override
131 public C run() throws IOException {
132 final A a = init.run();
133 try {
134 return body.f(a).run();
135 } catch (final IOException e) {
136 throw e;
137 } finally {
138 fin.f(a);
139 }
140 }
141 };
142 }
143
144 public static final <A> IO<A> unit(final A a) {
145 return new IO<A>() {
146 @Override
147 public A run() throws IOException {
148 return a;
149 }
150 };
151 }
152
153 public static final <A> IO<A> lazy(final P1<A> p) {
154 return () -> p._1();
155 }
156
157 public static final <A> IO<A> lazy(final F<Unit, A> f) {
158 return () -> f.f(Unit.unit());
159 }
160
161 public static final <A> SafeIO<A> lazySafe(final F<Unit, A> f) {
162 return () -> f.f(Unit.unit());
163 }
164
165 public static final <A> SafeIO<A> lazySafe(final P1<A> f) {
166 return () -> f._1();
167 }
168
169 /**
170 * A function that feeds an iteratee with lines read from a {@link BufferedReader}.
171 */
172 public static <A> F<BufferedReader, F<IterV<String, A>, IO<IterV<String, A>>>> lineReader() {
173 final F<IterV<String, A>, Boolean> isDone =
174 new F<Iteratee.IterV<String, A>, Boolean>() {
175 final F<P2<A, Input<String>>, P1<Boolean>> done = constant(P.p(true));
176 final F<F<Input<String>, IterV<String, A>>, P1<Boolean>> cont = constant(P.p(false));
177
178 @Override
179 public Boolean f(final IterV<String, A> i) {
180 return i.fold(done, cont)._1();
181 }
182 };
183
184 return new F<BufferedReader, F<IterV<String, A>, IO<IterV<String, A>>>>() {
185 @Override
186 public F<IterV<String, A>, IO<IterV<String, A>>> f(final BufferedReader r) {
187 return new F<IterV<String, A>, IO<IterV<String, A>>>() {
188 final F<P2<A, Input<String>>, P1<IterV<String, A>>> done = errorF("iteratee is done"); //$NON-NLS-1$
189
190 @Override
191 public IO<IterV<String, A>> f(final IterV<String, A> it) {
192 // use loop instead of recursion because of missing TCO
193 return new IO<Iteratee.IterV<String, A>>() {
194 @Override
195 public IterV<String, A> run() throws IOException {
196 IterV<String, A> i = it;
197 while (!isDone.f(i)) {
198 final String s = r.readLine();
199 if (s == null) { return i; }
200 final Input<String> input = Input.<String>el(s);
201 final F<F<Input<String>, IterV<String, A>>, P1<IterV<String, A>>> cont = F1Functions.lazy(Function.<Input<String>, IterV<String, A>>apply(input));
202 i = i.fold(done, cont)._1();
203 }
204 return i;
205 }
206 };
207 }
208 };
209 }
210 };
211 }
212
213 /**
214 * A function that feeds an iteratee with character chunks read from a {@link Reader}
215 * (char[] of size {@link #DEFAULT_BUFFER_SIZE}).
216 */
217 public static <A> F<Reader, F<IterV<char[], A>, IO<IterV<char[], A>>>> charChunkReader() {
218 final F<IterV<char[], A>, Boolean> isDone =
219 new F<Iteratee.IterV<char[], A>, Boolean>() {
220 final F<P2<A, Input<char[]>>, P1<Boolean>> done = constant(P.p(true));
221 final F<F<Input<char[]>, IterV<char[], A>>, P1<Boolean>> cont = constant(P.p(false));
222
223 @Override
224 public Boolean f(final IterV<char[], A> i) {
225 return i.fold(done, cont)._1();
226 }
227 };
228
229 return new F<Reader, F<IterV<char[], A>, IO<IterV<char[], A>>>>() {
230 @Override
231 public F<IterV<char[], A>, IO<IterV<char[], A>>> f(final Reader r) {
232 return new F<IterV<char[], A>, IO<IterV<char[], A>>>() {
233 final F<P2<A, Input<char[]>>, P1<IterV<char[], A>>> done = errorF("iteratee is done"); //$NON-NLS-1$
234
235 @Override
236 public IO<IterV<char[], A>> f(final IterV<char[], A> it) {
237 // use loop instead of recursion because of missing TCO
238 return new IO<Iteratee.IterV<char[], A>>() {
239 @Override
240 public IterV<char[], A> run() throws IOException {
241
242 IterV<char[], A> i = it;
243 while (!isDone.f(i)) {
244 char[] buffer = new char[DEFAULT_BUFFER_SIZE];
245 final int numRead = r.read(buffer);
246 if (numRead == -1) { return i; }
247 if(numRead < buffer.length) {
248 buffer = Arrays.copyOfRange(buffer, 0, numRead);
249 }
250 final Input<char[]> input = Input.<char[]>el(buffer);
251 final F<F<Input<char[]>, IterV<char[], A>>, P1<IterV<char[], A>>> cont =
252 F1Functions.lazy(Function.<Input<char[]>, IterV<char[], A>>apply(input));
253 i = i.fold(done, cont)._1();
254 }
255 return i;
256 }
257 };
258 }
259 };
260 }
261 };
262 }
263
264 /**
265 * A function that feeds an iteratee with characters read from a {@link Reader}
266 * (chars are read in chunks of size {@link #DEFAULT_BUFFER_SIZE}).
267 */
268 public static <A> F<Reader, F<IterV<Character, A>, IO<IterV<Character, A>>>> charChunkReader2() {
269 final F<IterV<Character, A>, Boolean> isDone =
270 new F<Iteratee.IterV<Character, A>, Boolean>() {
271 final F<P2<A, Input<Character>>, P1<Boolean>> done = constant(P.p(true));
272 final F<F<Input<Character>, IterV<Character, A>>, P1<Boolean>> cont = constant(P.p(false));
273
274 @Override
275 public Boolean f(final IterV<Character, A> i) {
276 return i.fold(done, cont)._1();
277 }
278 };
279
280 return new F<Reader, F<IterV<Character, A>, IO<IterV<Character, A>>>>() {
281 @Override
282 public F<IterV<Character, A>, IO<IterV<Character, A>>> f(final Reader r) {
283 return new F<IterV<Character, A>, IO<IterV<Character, A>>>() {
284 final F<P2<A, Input<Character>>, IterV<Character, A>> done = errorF("iteratee is done"); //$NON-NLS-1$
285
286 @Override
287 public IO<IterV<Character, A>> f(final IterV<Character, A> it) {
288 // use loop instead of recursion because of missing TCO
289 return new IO<Iteratee.IterV<Character, A>>() {
290 @Override
291 public IterV<Character, A> run() throws IOException {
292
293 IterV<Character, A> i = it;
294 while (!isDone.f(i)) {
295 char[] buffer = new char[DEFAULT_BUFFER_SIZE];
296 final int numRead = r.read(buffer);
297 if (numRead == -1) { return i; }
298 if(numRead < buffer.length) {
299 buffer = Arrays.copyOfRange(buffer, 0, numRead);
300 }
301 for(int c = 0; c < buffer.length; c++) {
302 final Input<Character> input = Input.el(buffer[c]);
303 final F<F<Input<Character>, IterV<Character, A>>, IterV<Character, A>> cont =
304 Function.<Input<Character>, IterV<Character, A>>apply(input);
305 i = i.fold(done, cont);
306 }
307 }
308 return i;
309 }
310 };
311 }
312 };
313 }
314 };
315 }
316
317 public static final <A, B> IO<B> map(final IO<A> io, final F<A, B> f) {
318 return new IO<B>() {
319 @Override
320 public B run() throws IOException {
321 return f.f(io.run());
322 }
323 };
324 }
325
326 public static final <A, B> IO<B> bind(final IO<A> io, final F<A, IO<B>> f) {
327 return new IO<B>() {
328 @Override
329 public B run() throws IOException {
330 return f.f(io.run()).run();
331 }
332 };
333 }
334
335 /**
336 * Evaluate each action in the sequence from left to right, and collect the results.
337 */
338 public static <A> IO<List<A>> sequence(List<IO<A>> list) {
339 F2<IO<A>, IO<List<A>>, IO<List<A>>> f2 = (io, ioList) ->
340 IOFunctions.bind(ioList, (xs) -> map(io, x -> List.cons(x, xs)));
341 return list.foldRight(f2, IOFunctions.unit(List.<A>nil()));
342 }
343
344
345 public static <A> IO<Stream<A>> sequence(Stream<IO<A>> stream) {
346 F2<IO<Stream<A>>, IO<A>, IO<Stream<A>>> f2 = (ioList, io) ->
347 IOFunctions.bind(ioList, (xs) -> map(io, x -> Stream.cons(x, P.lazy(u -> xs))));
348 return stream.foldLeft(f2, IOFunctions.unit(Stream.<A>nil()));
349 }
350
351
352 /**
353 * Map each element of a structure to an action, evaluate these actions from left to right
354 * and collect the results.
355 */
356 public static <A, B> IO<List<B>> traverse(List<A> list, F<A, IO<B>> f) {
357 F2<A, IO<List<B>>, IO<List<B>>> f2 = (a, acc) ->
358 bind(acc, (bs) -> map(f.f(a), b -> bs.append(List.list(b))));
359 return list.foldRight(f2, IOFunctions.unit(List.<B>nil()));
360 }
361
362 public static <A> IO<A> join(IO<IO<A>> io1) {
363 return bind(io1, io2 -> io2);
364 }
365
366 public static <A> SafeIO<Validation<IOException, A>> toSafeIO(IO<A> io) {
367 return () -> Try.f(() -> io.run())._1();
368 }
369
370 public static <A, B> IO<B> append(final IO<A> io1, final IO<B> io2) {
371 return () -> {
372 io1.run();
373 return io2.run();
374 };
375 }
376
377 public static <A, B> IO<A> left(final IO<A> io1, final IO<B> io2) {
378 return () -> {
379 A a = io1.run();
380 io2.run();
381 return a;
382 };
383 }
384
385 public static <A, B> IO<B> flatMap(final IO<A> io, final F<A, IO<B>> f) {
386 return bind(io, f);
387 }
388
389 static <A> IO<Stream<A>> sequenceWhile(final Stream<IO<A>> stream, final F<A, Boolean> f) {
390 return new IO<Stream<A>>() {
391 @Override
392 public Stream<A> run() throws IOException {
393 boolean loop = true;
394 Stream<IO<A>> input = stream;
395 Stream<A> result = Stream.<A>nil();
396 while (loop) {
397 if (input.isEmpty()) {
398 loop = false;
399 } else {
400 A a = input.head().run();
401 if (!f.f(a)) {
402 loop = false;
403 } else {
404 input = input.tail()._1();
405 result = result.cons(a);
406 }
407 }
408 }
409 return result.reverse();
410 }
411 };
412 }
413
414 public static <A, B> IO<B> apply(IO<A> io, IO<F<A, B>> iof) {
415 return bind(iof, f -> map(io, a -> f.f(a)));
416 }
417
418 public static <A, B, C> IO<C> liftM2(IO<A> ioa, IO<B> iob, F2<A, B, C> f) {
419 return bind(ioa, a -> map(iob, b -> f.f(a, b)));
420 }
421
422 public static <A> IO<List<A>> replicateM(IO<A> ioa, int n) {
423 return sequence(List.replicate(n, ioa));
424 }
425
426 public static <A> IO<State<BufferedReader, Validation<IOException, String>>> readerState() {
427 return () -> State.unit((BufferedReader r) -> P.p(r, Try.f((BufferedReader r2) -> r2.readLine()).f(r)));
428 }
429
430 public static final BufferedReader stdinBufferedReader = new BufferedReader(new InputStreamReader(System.in));
431
432 public static IO<String> stdinReadLine() {
433 return () -> stdinBufferedReader.readLine();
434 }
435
436 public static IO<Unit> stdoutPrintln(final String s) {
437 return () -> {
438 System.out.println(s);
439 return Unit.unit();
440 };
441 }
442
443 }