Skip to content

Instantly share code, notes, and snippets.

@hexclover
Created August 5, 2022 09:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hexclover/f378ed96fdb53fb4c6aa1093f9c1d5b4 to your computer and use it in GitHub Desktop.
Save hexclover/f378ed96fdb53fb4c6aa1093f9c1d5b4 to your computer and use it in GitHub Desktop.
Church numerals in C++ template (including subtraction)
#define testNum(num, val) \
do { \
static_assert(FromChurch<num>::get == (val)); \
static_assert(FromChurch<Pred::template Ap<num>::V>::get == \
((val) > 0 ? (val)-1 : 0)); \
} while (0)
#define testOp(op, a, b, exp) \
do { \
static_assert( \
FromChurch<op::template Ap<a>::V::template Ap<b>::V>::get == \
(exp)); \
} while (0)
#define testAB(op, a, b, exp) \
do { \
testOp(op##A, a, b, exp); \
testOp(op##B, a, b, exp); \
} while (0)
struct Id {
template <class X>
struct Ap {
using V = X;
};
};
struct Zero {
template <class _>
struct Ap {
using V = Id;
};
};
struct Succ {
template <class M>
struct Ap {
struct V {
template <class F>
struct Ap {
struct V {
template <class a>
struct Ap {
// Compiling with using Ap = ... makes g++ stuck.
// Why?
using V = typename M::template Ap<F>::V::template Ap<
typename F::template Ap<a>::V>::V;
};
};
};
};
};
};
class Pred {
private:
// A wrapper contains a value X (here X's are Church numerals), and is a
// function that receives another function F as an argument.
// We have two kinds of wrappers. The first kind, when called, returns the
// wrapped value x, ignoring the argument F. In contrast, the second kind
// will return F x.
// The initial wrapper is of the first kind.
struct CZero {
template <class _>
struct Ap {
using V = Zero;
};
};
// The function Inc increases the wrapped Church numeral by one, and returns
// a new wrapper: Inc m = \f -> f (m succ)
// The new wrapper is of the second kind --- it will apply the argument F
// to the inner value before returning.
struct Inc {
template <class X>
struct Ap {
struct V {
template <class F>
using Ap =
typename F::template Ap<typename X::template Ap<Succ>::V>;
};
};
};
struct Extract {
template <class M>
using Ap = typename M::template Ap<Id>;
};
public:
template <class M>
using Ap = typename Extract::template Ap<
typename M::template Ap<Inc>::V::template Ap<CZero>::V>;
};
using One = Succ::template Ap<Zero>::V;
using Two = Succ::template Ap<One>::V;
using Three = Succ::template Ap<Two>::V;
using Four = Succ::template Ap<Three>::V;
using Five = Succ::template Ap<Four>::V;
using Six = Succ::template Ap<Five>::V;
struct AddA {
template <class M>
struct Ap {
struct V {
template <class N>
struct Ap {
struct V {
template <class F>
struct Ap {
struct V {
template <class a>
struct Ap {
using V =
typename M::template Ap<F>::V::template Ap<
typename N::template Ap<
F>::V::template Ap<a>::V>::V;
};
};
};
};
};
};
};
};
struct MulA {
template <class M>
struct Ap {
struct V {
template <class N>
struct Ap {
struct V {
template <class F>
struct Ap {
using V = typename M::template Ap<
typename N::template Ap<F>::V>::V;
};
};
};
};
};
};
struct PowA {
template <class M>
struct Ap {
struct V {
template <class N>
struct Ap {
using V = typename N::template Ap<M>::V;
};
};
};
};
// We can also define "higher-level" operations in terms of "lower-level" ones.
struct AddB {
template <class M>
using Ap = typename M::template Ap<Succ>;
};
struct Sub {
template <class M>
struct Ap {
struct V {
template <class N>
using Ap = typename N::template Ap<Pred>::V::template Ap<M>;
};
};
};
struct MulB {
template <class M>
struct Ap {
struct V {
template <class N>
using Ap = typename M::template Ap<
typename AddB::template Ap<N>::V>::V::template Ap<Zero>;
};
};
};
struct PowB {
template <class M>
struct Ap {
struct V {
template <class N>
using Ap = typename N::template Ap<
typename MulB::template Ap<M>::V>::V::template Ap<One>;
};
};
};
template <class M>
class FromChurch {
private:
using LL = long long;
template <LL n>
struct Int {
enum : LL { get = n };
};
struct AddOne {
template <class N>
struct Ap {
using V = Int<N::get + 1>;
};
};
public:
enum : LL { get = M::template Ap<AddOne>::V::template Ap<Int<0>>::V::get };
};
int main() {
testNum(Zero, 0);
testNum(One, 1);
testNum(Two, 2);
testAB(Add, Zero, One, 1);
testAB(Add, Two, Zero, 2);
testAB(Add, Three, Two, 5);
testAB(Mul, Zero, Five, 0);
testAB(Mul, Two, Zero, 0);
testAB(Mul, Two, Three, 6);
testAB(Pow, Zero, Five, 0);
testAB(Pow, Six, Zero, 1);
testAB(Pow, Three, Six, 729);
testOp(Sub, Zero, One, 0);
testOp(Sub, One, Zero, 1);
testOp(Sub, One, One, 0);
testOp(Sub, Three, One, 2);
testOp(Sub, Three, Six, 0);
testOp(Sub, Six, Two, 4);
testOp(Sub, Six, Five, 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment