LoginSignup
2
2

More than 5 years have passed since last update.

FunctionalInterfaceなTuple(Eitherもあるよ)

Last updated at Posted at 2016-07-10

そもそも@FunctionalInterfaceである必要はあるのか

ないです。セミコロンの少なさと括弧の多さを楽しむものと思ってください。

普通はここの記事のものやFunctionnal JavaP(roducts)を使うのが良いと思います。

追記: Java8で使うならそのものずばりのJavaTuplesのほうが使いやすいかも。

Tupleの前に

interface だと toStringequals が実装できないので仕込みが必要です。

Show.java
@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));
    }
}
Eq.java
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; の気持ち悪さはどうにかならないのか。

Tuple.java
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が必要になります。

Either.java
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 の時間だ!

リストはこうなります。

Lst.java
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) も書くかもしれません。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2