Created
August 5, 2022 09:29
-
-
Save hexclover/f378ed96fdb53fb4c6aa1093f9c1d5b4 to your computer and use it in GitHub Desktop.
Church numerals in C++ template (including subtraction)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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