Mercurial > hg > Members > tatsuki > functionaljava-master > core
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 } |