import java.io.*;
import java.util.*;
import java.util.function.*;
class Reader<R, A> {
private final Function<R, A> k;
run(final R r) {
A return this.k.apply(r);
}
Reader(final Function<R, A> k) {
this.k = k;
}
static <A, B> Reader<A, B> fromFunction(final Function<A, B> k) {
return new Reader<>((a) -> k.apply(a));
}
static <A> Reader<A, Void> fromConsumer(final Consumer<A> k) {
return new Reader<>((a) -> {
.accept(a);
kreturn null;
});
}
<R2 extends R, B> Reader<R2, B> map(final Function<A, B> f) {
return new Reader<>((r) -> f.apply(run(r)));
}
<R2 extends R, B> Reader<R2, B> flatMap(final Function<A, Reader<R2, B>> f) {
return new Reader<>((r) -> f.apply(run(r)).run(r));
}
<R2 extends R, B> Reader<R2, B> andThen(final Reader<R2, B> next) {
return new Reader<>((r) -> {
run(r);
return next.run(r);
});
}
}
interface GetEnv {
Map<String, String> getEnv();
static <R extends GetEnv> Reader<R, Map<String, String>> of() {
return Reader.fromFunction((r) -> r.getEnv());
}
}
interface ReadLine {
String readLine();
static <R extends ReadLine> Reader<R, String> of() {
return Reader.fromFunction((r) -> r.readLine());
}
}
interface WriteLine {
void writeLine(final String line);
static <R extends WriteLine> Reader<R, Void> of(final String line) {
return Reader.fromConsumer((r) -> r.writeLine(line));
}
}
class Greeter {
static <R extends ReadLine & WriteLine> Reader<R, Void> esProgram() {
return WriteLine.of("¿Cómo te llamas?")
.andThen(ReadLine.of())
.flatMap((name) -> WriteLine.of(String.format("¡Hola, %s!", name)));
}
static <R extends GetEnv & ReadLine & WriteLine> Reader<R, Void> enProgram() {
return WriteLine.of("What is your name?")
.andThen(ReadLine.of())
.flatMap((name) -> WriteLine.of(String.format("Hello, %s!", name)));
}
static <R extends GetEnv & ReadLine & WriteLine> Reader<R, Void> program() {
return GetEnv.of()
.flatMap((env) -> {
final String lang =
.ofNullable(env.get("LANG"))
Optional.map((x) -> x.substring(0, 2))
.orElse("en");
switch (lang) {
case "es": return esProgram();
default: return enProgram();
}
});
}
}
class Live {
interface Env extends GetEnv, ReadLine, WriteLine { }
static Env env =
new Env() {
@Override
public Map<String, String> getEnv() {
return System.getenv();
}
@Override
public String readLine() {
final java.io.BufferedReader r =
new BufferedReader(
new InputStreamReader(System.in));
try {
return r.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void writeLine(final String line) {
System.out.println(line);
}
};
}
class Test {
static final LinkedList<String> output =
new LinkedList<>();
static final LinkedList<String> input =
new LinkedList<>(Arrays.asList("James"));
interface Env extends GetEnv, ReadLine, WriteLine { }
static Env env =
new Env() {
@Override
public Map<String, String> getEnv() {
return Map.ofEntries(Map.entry("LANG", "en_US.UTF-8"));
}
@Override
public String readLine() {
return input.pop();
}
@Override
public void writeLine(final String line) {
.push(line);
output}
};
}
public class Main {
public static void main(String[] args) {
if ("live".equalsIgnoreCase(System.getenv("ENV"))) {
.program().run(Live.env);
Greeter} else {
.program().run(Test.env);
GreeterSystem.out.println(String.format("produced: %s", Test.output));
System.out.println(String.format("unconsumed input: %s", Test.input));
}
}
}
This file is literate Java, and can be run using Codedown:
$ curl https://earldouglas.com/posts/effect-systems/reader-java.md |
codedown java > Main.java
$ javac Main.java
$ ENV=live LANG=es java Main
¿Cómo te llamas?
James
¡Hola, James!