そもそも@FunctionalInterface
である必要はあるのか
ないです。セミコロンの少なさと括弧の多さを楽しむものと思ってください。
普通はここの記事のものやFunctionnal JavaのP(roducts)を使うのが良いと思います。
追記: Java8で使うならそのものずばりのJavaTuplesのほうが使いやすいかも。
Tupleの前に
interface だと toString
と equals
が実装できないので仕込みが必要です。
@FunctionalInterface
public interface Show {
String show();
static String show(Object object) {
return object instanceof Show
? Show.class.cast(object).show()
: java.util.Objects.toString(object, "");
}
static void print(Object object) {
System.out.print(show(object));
}
static void println(Object object) {
System.out.println(show(object));
}
}
import java.util.function.Supplier;
@FunctionalInterface
public interface Eq {
boolean eq(Eq that);
static boolean eq(Object o1, Object o2) {
return o1 instanceof Eq
&& o2 instanceof Eq
? Eq.class.cast(o1).eq(Eq.class.cast(o2))
: o1 != null && o2 != null && o1.equals(o2)
|| o1 == o2;
}
static <T extends Eq & Show> Supplier<T> concrete(final T self) {
return new Supplier<T>() {
@Override
public T get() {
return self;
}
@Override
public boolean equals(Object that) {
return eq(self, that);
}
@Override
public int hashCode() {
return self.show().hashCode();
}
};
}
}
Tuple!
Consumer -> Function の変換テクニックはいろいろ応用が効きそうです。
しかしこの private X value;
の気持ち悪さはどうにかならないのか。
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@FunctionalInterface
public interface Tuple<F, S> extends
Show, Eq, Consumer<BiConsumer<? super F, ? super S>> {
default <X> X apply(BiFunction<F, S, X> f) {
return new Supplier<X>() {
private X value;
public X get() {
accept((fst, snd) -> value = f.apply(fst, snd));
return value;
}
}.get();
}
default <X> X apply(Function<F, Function<S, X>> f) {
return this.<X>apply((fst, snd) -> f.apply(fst).apply(snd));
}
default F fst() {
return apply((fst, snd) -> fst);
}
default S snd() {
return apply((fst, snd) -> snd);
}
default boolean eq(Eq that) {
return that instanceof Tuple
&& eq(Tuple.class.cast(that));
}
default boolean eq(Tuple<?, ?> that) {
return Eq.eq(this.fst(), that.fst())
&& Eq.eq(this.snd(), that.snd());
}
default String show() {
return String.format("(%s,%s)", Show.show(fst()),
Show.show(snd()));
}
static <F, S> Tuple<F, S> of(F fst, S snd) {
return c -> c.accept(fst, snd);
}
static void main(String[] args) {
System.out
.println(Show.show(of("foo", of(1, true))));
System.out.println(Eq.eq(
of("foo", of(1, true)),
of("foo", of(1, true))));
System.out.println(Eq.eq(
of("foo", of(1, true)),
of("foo", of(2, true))));
}
}
ちゃんと Tuple してます。
(foo,(1,true))
true
false
Tuple ときたら zip ...の前に
Streamからいきなり zip しようとすると Iterator を扱うはめになって Functional ではないので Tuple を利用したリストを作ります。中身の有無を示すためにまずはEitherが必要になります。
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@FunctionalInterface
public interface Either<L,R>
extends Show, Eq, BiConsumer<Consumer<L>,Consumer<R>>{
default <X> X resolve(Function<L,X> fL, Function<R,X> fR) {
return new Supplier<X>() {
private X value;
public X get() {
accept(v -> value = fL.apply(v),
v -> value = fR.apply(v));
return value;
}
}.get();
}
default String show() {
return resolve(Show::show, Show::show);
}
default boolean eq(Eq that) {
return that instanceof Either
&& eq(Either.class.cast(that));
}
default boolean eq(Either<?,?> that) {
return resolve(L1 -> that.resolve(
L2 -> Eq.eq(L1, L2)
, R2 -> false)
, R1 -> that.resolve(
L2 -> false
, R2 -> Eq.eq(R1, R2)));
}
static <L,R> Either<L,R> L(L value) {
return (cL, cR) -> cL.accept(value);
}
static <L,R> Either<L,R> R(R value) {
return (cL, cR) -> cR.accept(value);
}
static void main(String[] args) {
System.out.println(Either.L("fail").show());
System.out.println(Either.R(1).show());
System.out.println(Eq.eq(Either.R(1), Either.R(1)));
System.out.println(Eq.eq(Either.L("fail"), Either.R(1)));
System.out.println(Eq.eq(Either.L("fail"), Either.L("fail")));
}
}
fail
1
true
false
true
Tupleの兄弟のようなコードですがやっぱり private X value;
が実にの気持ち悪いです。
さあ zip の時間だ!
リストはこうなります。
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
@FunctionalInterface
public interface Lst<I> extends
Either<Function<I, I>, Tuple<I, Lst<I>>>, Function<I, Lst<I>> {
default Lst<I> apply(I i) {
return (cL, cR) -> cR.accept(c -> c.accept(i, this));
}
default <O> Function<O, O> foldR(BiFunction<O, I, O> f) {
return resolve(
eL -> Function.identity(),
eR -> eR.apply(
fst ->
snd -> snd.foldR(f).compose(o -> f.apply(o, fst))));
}
default <O> Function<O, O> foldL(BiFunction<O, I, O> f) {
return resolve(
eL -> Function.identity(),
eR -> eR.apply(
fst ->
snd -> snd.foldL(f).andThen(o -> f.apply(o, fst))));
}
default Stream<I> toStreamR() {
return this.<Stream.Builder<I>> foldL(
(is, i) -> is.add(i)).apply(Stream.builder()).build();
}
default Stream<I> toStreamL() {
return this.<Stream.Builder<I>> foldR(
(is, i) -> is.add(i)).apply(Stream.builder()).build();
}
default String show() {
return resolve(eL -> "()", eR -> eR.show());
}
static <I> Lst<I> of() {
return (cL, cR) -> cL.accept(Function.identity());
}
static <I> Lst<I> of(I i) {
return (cL, cR) -> cR.accept(c -> c.accept(i, of()));
}
static <I> Lst<I> of(Stream<I> stream) {
return stream.<Function<Lst<I>, Lst<I>>>map(i -> s -> s.apply(i))
.reduce(Function.identity(), (f, g) -> f.compose(g)).apply(of());
}
static <I> Lst<I> reverseOf(Stream<I> stream) {
return stream.<Function<Lst<I>, Lst<I>>>map(i -> s -> s.apply(i))
.reduce(Function.identity(), (f, g) -> f.andThen(g)).apply(of());
}
static <F,S> Lst<Tuple<F,S>> zip(Lst<F> f, Lst<S> s) {
return f.resolve(fL -> Lst.of(), fR ->
s.resolve(sL -> Lst.of(), sR ->
zip(fR.snd(), sR.snd()).apply(Tuple.of(fR.fst(), sR.fst()))));
}
static void main(String[] args) {
System.out.println(Eq.eq(Lst.of(), Lst.of()));
System.out.println(Lst.of(Stream.of(1, 2, 3, 4, 5)).show());
System.out.println(Lst.reverseOf(Stream.of("foo", "bar", "baz")).show());
Lst.<Integer> of()
.apply(1)
.apply(2)
.apply(3)
.apply(4)
.apply(5)
.toStreamL()
.forEach(System.out::print);
Lst.<String> of()
.apply("\n")
.apply("foo")
.apply("bar")
.apply("baz")
.apply("\n")
.toStreamR()
.forEach(System.out::print);
zip(Lst.of(Stream.of("foo", "bar", "baz")),
zip(Lst.of(Stream.of(1, 2, 3, 4, 5)),
Lst.of(Stream.of(true, false, true, false))))
.toStreamL().forEach(Show::println);
}
}
true
(1,(2,(3,(4,(5,())))))
(baz,(bar,(foo,())))
54321
foobarbaz
(foo,(1,true))
(bar,(2,false))
(baz,(3,true))
ちゃんとリストもzipもしてます。
apply で先頭が伸びるのと toStream().forEch(System.out::print) と出来無いのはご愛嬌。
おわりに
セミコロンレスは変態の所業だと思いますが@FunctionalInterface
縛り(フィールド変数使えないprivate X value;
がフィールド変数でした)は結構楽しいかもしれません。
現状では java.util.Map のキーに使えない欠点があるのでそのうち <X> X lookup(Lst<Tuple<Eq,X>> map, Eq key)
も書くかもしれません。