Primitive.java
package com.acme.symbolic;

import java.util.function.Function;

import static church.lang.operators.Streams.$$encode;

@SuppressWarnings("unchecked")
public abstract class Primitive {
    public interface Visitor<S> {
        default S get(Primitive receiver) {
            S result = receiver.accept(this);
            return result == null ? defaultImplementation(receiver) : result;
        }

        default S getOrDefault(Primitive receiver, S defaultValue) {
            S result = receiver.accept(this);
            return result == null ? defaultValue : result;
        }

        default S integer(int i) {
            return null;
        }

        default S proc(Function<Primitive, Primitive> fn) {
            return null;
        }

        default S defaultImplementation(Primitive e) {
            throw new UnsupportedOperationException();
        }
    }

    public abstract <S> S accept(Visitor<S> visitor);

    public static Primitive integer(int i) {
        return new Primitive() {
            public <S> S accept(Visitor<S> visitor) {
                return visitor.integer(i);
            }
        };
    }

    public static Primitive proc(Function<Primitive, Primitive> fn) {
        return new Primitive() {
            public <S> S accept(Visitor<S> visitor) {
                return visitor.proc(fn);
            }
        };
    }

    public static <T> $$encode<T, Primitive> $encode($$encode<T, Integer> $L0, $$encode<T, String> $L1) {
        return (stream, $0) -> new Visitor<T>() {
            public T integer(int i) {
                return $L0.$encode(stream, i);
            }

            public T proc(Function<Primitive, Primitive> fn) {
                return $L1.$encode(stream, "<function>");
            }

        }
                .get($0);
    }

}