Skip to content

Commit ce53b9f

Browse files
committed
Made IO compatible with lambdas, moved methods to IOFunctions
1 parent 478e251 commit ce53b9f

File tree

3 files changed

+335
-315
lines changed

3 files changed

+335
-315
lines changed

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

Lines changed: 20 additions & 310 deletions
Original file line numberDiff line numberDiff line change
@@ -1,310 +1,20 @@
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.F;
17-
import fj.F1Functions;
18-
import fj.Function;
19-
import fj.P;
20-
import fj.P1;
21-
import fj.P2;
22-
import fj.Unit;
23-
import fj.data.Iteratee.Input;
24-
import fj.data.Iteratee.IterV;
25-
26-
/**
27-
* IO monad for processing files, with main methods {@link #enumFileLines },
28-
* {@link #enumFileChars} and {@link #enumFileCharChunks}
29-
* (the latter one is the fastest as char chunks read from the file are directly passed to the iteratee
30-
* without indirection in between).
31-
*
32-
* @author Martin Grotzke
33-
*
34-
* @param <A> the type of the result produced by the wrapped iteratee
35-
*/
36-
public abstract class IO<A> {
37-
38-
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
39-
40-
public static final F<Reader, IO<Unit>> closeReader =
41-
new F<Reader, IO<Unit>>() {
42-
@Override
43-
public IO<Unit> f(final Reader r) {
44-
return closeReader(r);
45-
}
46-
};
47-
48-
public static IO<Unit> closeReader(final Reader r) {
49-
return new IO<Unit>() {
50-
@Override
51-
public Unit run() throws IOException {
52-
r.close();
53-
return Unit.unit();
54-
}
55-
};
56-
}
57-
58-
/**
59-
* An IO monad that reads lines from the given file (using a {@link BufferedReader}) and passes
60-
* lines to the provided iteratee. May not be suitable for files with very long
61-
* lines, consider to use {@link #enumFileCharChunks} or {@link #enumFileChars}
62-
* as an alternative.
63-
*
64-
* @param f the file to read, must not be <code>null</code>
65-
* @param encoding the encoding to use, {@link Option#none()} means platform default
66-
* @param i the iteratee that is fed with lines read from the file
67-
*/
68-
public static <A> IO<IterV<String, A>> enumFileLines(final File f, final Option<Charset> encoding, final IterV<String, A> i) {
69-
return bracket(bufferedReader(f, encoding)
70-
, Function.<BufferedReader, IO<Unit>>vary(closeReader)
71-
, partialApply2(IO.<A>lineReader(), i));
72-
}
73-
74-
/**
75-
* An IO monad that reads char chunks from the given file and passes them to the given iteratee.
76-
*
77-
* @param f the file to read, must not be <code>null</code>
78-
* @param encoding the encoding to use, {@link Option#none()} means platform default
79-
* @param i the iteratee that is fed with char chunks read from the file
80-
*/
81-
public static <A> IO<IterV<char[], A>> enumFileCharChunks(final File f, final Option<Charset> encoding, final IterV<char[], A> i) {
82-
return bracket(fileReader(f, encoding)
83-
, Function.<Reader, IO<Unit>>vary(closeReader)
84-
, partialApply2(IO.<A>charChunkReader(), i));
85-
}
86-
87-
/**
88-
* An IO monad that reads char chunks from the given file and passes single chars to the given iteratee.
89-
*
90-
* @param f the file to read, must not be <code>null</code>
91-
* @param encoding the encoding to use, {@link Option#none()} means platform default
92-
* @param i the iteratee that is fed with chars read from the file
93-
*/
94-
public static <A> IO<IterV<Character, A>> enumFileChars(final File f, final Option<Charset> encoding, final IterV<Character, A> i) {
95-
return bracket(fileReader(f, encoding)
96-
, Function.<Reader, IO<Unit>>vary(closeReader)
97-
, partialApply2(IO.<A>charChunkReader2(), i));
98-
}
99-
100-
public static IO<BufferedReader> bufferedReader(final File f, final Option<Charset> encoding) {
101-
return fileReader(f, encoding).map(new F<Reader, BufferedReader>() {
102-
@Override
103-
public BufferedReader f(final Reader a) {
104-
return new BufferedReader(a);
105-
}});
106-
}
107-
108-
public static IO<Reader> fileReader(final File f, final Option<Charset> encoding) {
109-
return new IO<Reader>() {
110-
@Override
111-
public Reader run() throws IOException {
112-
final FileInputStream fis = new FileInputStream(f);
113-
return encoding.isNone() ? new InputStreamReader(fis) : new InputStreamReader(fis, encoding.some());
114-
}
115-
};
116-
}
117-
118-
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) {
119-
return new IO<C>() {
120-
@Override
121-
public C run() throws IOException {
122-
final A a = init.run();
123-
try {
124-
return body.f(a).run();
125-
} catch (final IOException e) {
126-
throw e;
127-
} finally {
128-
fin.f(a);
129-
}
130-
}
131-
};
132-
}
133-
134-
public static final <A> IO<A> unit(final A a) {
135-
return new IO<A>() {
136-
@Override
137-
public A run() throws IOException {
138-
return a;
139-
}
140-
};
141-
}
142-
143-
/**
144-
* A function that feeds an iteratee with lines read from a {@link BufferedReader}.
145-
*/
146-
public static <A> F<BufferedReader, F<IterV<String, A>, IO<IterV<String, A>>>> lineReader() {
147-
final F<IterV<String, A>, Boolean> isDone =
148-
new F<Iteratee.IterV<String, A>, Boolean>() {
149-
final F<P2<A, Input<String>>, P1<Boolean>> done = constant(P.p(true));
150-
final F<F<Input<String>, IterV<String, A>>, P1<Boolean>> cont = constant(P.p(false));
151-
152-
@Override
153-
public Boolean f(final IterV<String, A> i) {
154-
return i.fold(done, cont)._1();
155-
}
156-
};
157-
158-
return new F<BufferedReader, F<IterV<String, A>, IO<IterV<String, A>>>>() {
159-
@Override
160-
public F<IterV<String, A>, IO<IterV<String, A>>> f(final BufferedReader r) {
161-
return new F<IterV<String, A>, IO<IterV<String, A>>>() {
162-
final F<P2<A, Input<String>>, P1<IterV<String, A>>> done = errorF("iteratee is done"); //$NON-NLS-1$
163-
164-
@Override
165-
public IO<IterV<String, A>> f(final IterV<String, A> it) {
166-
// use loop instead of recursion because of missing TCO
167-
return new IO<Iteratee.IterV<String, A>>() {
168-
@Override
169-
public IterV<String, A> run() throws IOException {
170-
IterV<String, A> i = it;
171-
while (!isDone.f(i)) {
172-
final String s = r.readLine();
173-
if (s == null) { return i; }
174-
final Input<String> input = Input.<String>el(s);
175-
final F<F<Input<String>, IterV<String, A>>, P1<IterV<String, A>>> cont = F1Functions.lazy(Function.<Input<String>, IterV<String, A>>apply(input));
176-
i = i.fold(done, cont)._1();
177-
}
178-
return i;
179-
}
180-
};
181-
}
182-
};
183-
}
184-
};
185-
}
186-
187-
/**
188-
* A function that feeds an iteratee with character chunks read from a {@link Reader}
189-
* (char[] of size {@link #DEFAULT_BUFFER_SIZE}).
190-
*/
191-
public static <A> F<Reader, F<IterV<char[], A>, IO<IterV<char[], A>>>> charChunkReader() {
192-
final F<IterV<char[], A>, Boolean> isDone =
193-
new F<Iteratee.IterV<char[], A>, Boolean>() {
194-
final F<P2<A, Input<char[]>>, P1<Boolean>> done = constant(P.p(true));
195-
final F<F<Input<char[]>, IterV<char[], A>>, P1<Boolean>> cont = constant(P.p(false));
196-
197-
@Override
198-
public Boolean f(final IterV<char[], A> i) {
199-
return i.fold(done, cont)._1();
200-
}
201-
};
202-
203-
return new F<Reader, F<IterV<char[], A>, IO<IterV<char[], A>>>>() {
204-
@Override
205-
public F<IterV<char[], A>, IO<IterV<char[], A>>> f(final Reader r) {
206-
return new F<IterV<char[], A>, IO<IterV<char[], A>>>() {
207-
final F<P2<A, Input<char[]>>, P1<IterV<char[], A>>> done = errorF("iteratee is done"); //$NON-NLS-1$
208-
209-
@Override
210-
public IO<IterV<char[], A>> f(final IterV<char[], A> it) {
211-
// use loop instead of recursion because of missing TCO
212-
return new IO<Iteratee.IterV<char[], A>>() {
213-
@Override
214-
public IterV<char[], A> run() throws IOException {
215-
216-
IterV<char[], A> i = it;
217-
while (!isDone.f(i)) {
218-
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
219-
final int numRead = r.read(buffer);
220-
if (numRead == -1) { return i; }
221-
if(numRead < buffer.length) {
222-
buffer = Arrays.copyOfRange(buffer, 0, numRead);
223-
}
224-
final Input<char[]> input = Input.<char[]>el(buffer);
225-
final F<F<Input<char[]>, IterV<char[], A>>, P1<IterV<char[], A>>> cont =
226-
F1Functions.lazy(Function.<Input<char[]>, IterV<char[], A>>apply(input));
227-
i = i.fold(done, cont)._1();
228-
}
229-
return i;
230-
}
231-
};
232-
}
233-
};
234-
}
235-
};
236-
}
237-
238-
/**
239-
* A function that feeds an iteratee with characters read from a {@link Reader}
240-
* (chars are read in chunks of size {@link #DEFAULT_BUFFER_SIZE}).
241-
*/
242-
public static <A> F<Reader, F<IterV<Character, A>, IO<IterV<Character, A>>>> charChunkReader2() {
243-
final F<IterV<Character, A>, Boolean> isDone =
244-
new F<Iteratee.IterV<Character, A>, Boolean>() {
245-
final F<P2<A, Input<Character>>, P1<Boolean>> done = constant(P.p(true));
246-
final F<F<Input<Character>, IterV<Character, A>>, P1<Boolean>> cont = constant(P.p(false));
247-
248-
@Override
249-
public Boolean f(final IterV<Character, A> i) {
250-
return i.fold(done, cont)._1();
251-
}
252-
};
253-
254-
return new F<Reader, F<IterV<Character, A>, IO<IterV<Character, A>>>>() {
255-
@Override
256-
public F<IterV<Character, A>, IO<IterV<Character, A>>> f(final Reader r) {
257-
return new F<IterV<Character, A>, IO<IterV<Character, A>>>() {
258-
final F<P2<A, Input<Character>>, IterV<Character, A>> done = errorF("iteratee is done"); //$NON-NLS-1$
259-
260-
@Override
261-
public IO<IterV<Character, A>> f(final IterV<Character, A> it) {
262-
// use loop instead of recursion because of missing TCO
263-
return new IO<Iteratee.IterV<Character, A>>() {
264-
@Override
265-
public IterV<Character, A> run() throws IOException {
266-
267-
IterV<Character, A> i = it;
268-
while (!isDone.f(i)) {
269-
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
270-
final int numRead = r.read(buffer);
271-
if (numRead == -1) { return i; }
272-
if(numRead < buffer.length) {
273-
buffer = Arrays.copyOfRange(buffer, 0, numRead);
274-
}
275-
for(int c = 0; c < buffer.length; c++) {
276-
final Input<Character> input = Input.el(buffer[c]);
277-
final F<F<Input<Character>, IterV<Character, A>>, IterV<Character, A>> cont =
278-
Function.<Input<Character>, IterV<Character, A>>apply(input);
279-
i = i.fold(done, cont);
280-
}
281-
}
282-
return i;
283-
}
284-
};
285-
}
286-
};
287-
}
288-
};
289-
}
290-
291-
public abstract A run() throws IOException;
292-
293-
public final <B> IO<B> map(final F<A, B> f) {
294-
return new IO<B>() {
295-
@Override
296-
public B run() throws IOException {
297-
return f.f(IO.this.run());
298-
}
299-
};
300-
}
301-
302-
public final <B> IO<B> bind(final F<A, IO<B>> f) {
303-
return new IO<B>() {
304-
@Override
305-
public B run() throws IOException {
306-
return f.f(IO.this.run()).run();
307-
}
308-
};
309-
}
310-
}
1+
package fj.data;
2+
3+
/**
4+
* Created by MarkPerry on 19/06/2014.
5+
*/
6+
7+
import java.io.IOException;
8+
9+
/**
10+
* IO monad for processing files
11+
*
12+
* @author Martin Grotzke
13+
*
14+
* @param <A> the type of the result produced by the IO
15+
*/
16+
public interface IO<A> {
17+
18+
public A run() throws IOException;
19+
20+
}

0 commit comments

Comments
 (0)