ExprTest.java
package com.acme.symbolic;

import church.lang.ByteStream;
import church.lang.operators.Streams.$$encode;
import church.primitives.Integers;
import church.primitives.Objects;

import static church.lang.operators.Streams.output;
import static com.acme.symbolic.Expr.*;
import static com.acme.symbolic.Primitive.*;

@SuppressWarnings("unchecked")
public class ExprTest {
    private static final $$encode<ByteStream, String>    $S0 = Objects::$encode;
    private static final $$encode<ByteStream, Integer>   $S1 = Integers::$encode;
    private static final $$encode<ByteStream, Primitive> $S2 = Primitive.$encode($S1, $S0);
    private static final $$encode<ByteStream, Expr>      $S3 = Expr.$encode($S0, $S2);

    public static ByteStream test(Expr input) {
        return $S0.$encode($S2.$encode($S0.$encode($S3.$encode(output, input), " = "), eval(input)), "\n");
    }

    public static void main(String[] args) {
        Expr zero = lambda("f", lambda("x", var("x")));
        Expr one  = lambda("x", var("x"));
        Expr two  = lambda("f", lambda("x", app(var("f"), app(var("f"), var("x")))));
        Expr i_0  = $const(integer(0));
        Expr i_succ = $const(proc(arg -> new Primitive.Visitor<Primitive>() {
            public Primitive integer(int i) {
                return Primitive.integer((i + 1));
            }

        }
                .get(arg)));
        test(app(app(app(two, two), i_succ), i_0));
        test(app(app(app(two, app(two, two)), i_succ), i_0));
        test(app(app(app(app(app(two, two), two), two), i_succ), i_0));
    }

}