Note: The mercurial server is disabled at the moment while I investigate whether it can run with an acceptably low CPU load – Mike.
Twice twice twice
Q: What does the function twice twice twice do?
Tracing the expansion
A: Consider the following Haskell program.
data Exp = Ap Exp Exp | Twice | F | X pretty :: Exp -> String pretty (Ap e1 e2) = pretty e1 ++ " " ++ pretty2 e2 pretty e = pretty2 e pretty2 :: Exp -> String pretty2 Twice = "twice" pretty2 F = "f" pretty2 X = "x" pretty2 e = "(" ++ pretty e ++ ")" reduce :: Exp -> Maybe Exp reduce (Ap (Ap Twice f) x) = return (Ap f (Ap f x)) reduce (Ap F x) = do x' <- reduce x; return (Ap F x') reduce (Ap g x) = do g' <- reduce g; return (Ap g' x) reduce e = Nothing trace :: (a -> Maybe a) -> a -> [a] trace f x = tr (Just x) where tr Nothing = [] tr (Just x) = x : tr (f x) foo :: Exp foo = Ap (Ap (Ap (Ap Twice Twice) Twice) F) X main = putStr (unlines (map pretty (trace reduce foo)))
Its output is as follows.
twice twice twice f x twice (twice twice) f x twice twice (twice twice f) x twice (twice (twice twice f)) x twice (twice twice f) (twice (twice twice f) x) twice twice f (twice twice f (twice (twice twice f) x)) twice (twice f) (twice twice f (twice (twice twice f) x)) twice f (twice f (twice twice f (twice (twice twice f) x))) f (f (twice f (twice twice f (twice (twice twice f) x)))) f (f (f (f (twice twice f (twice (twice twice f) x))))) f (f (f (f (twice (twice f) (twice (twice twice f) x))))) f (f (f (f (twice f (twice f (twice (twice twice f) x)))))) f (f (f (f (f (f (twice f (twice (twice twice f) x))))))) f (f (f (f (f (f (f (f (twice (twice twice f) x)))))))) f (f (f (f (f (f (f (f (twice twice f (twice twice f x))))))))) f (f (f (f (f (f (f (f (twice (twice f) (twice twice f x))))))))) f (f (f (f (f (f (f (f (twice f (twice f (twice twice f x)))))))))) f (f (f (f (f (f (f (f (f (f (twice f (twice twice f x))))))))))) f (f (f (f (f (f (f (f (f (f (f (f (twice twice f x)))))))))))) f (f (f (f (f (f (f (f (f (f (f (f (twice (twice f) x)))))))))))) f (f (f (f (f (f (f (f (f (f (f (f (twice f (twice f x))))))))))))) f (f (f (f (f (f (f (f (f (f (f (f (f (f (twice f x)))))))))))))) f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f x)))))))))))))))
Plainly, therefore, twice twice twice f x computes f16 x
.
Any number of twices
Generalizing, we might ask about the function twice twice ... twice with n occurrences of twice.
Here's a note that gives a proof that this function, when applied to f and x, computes f2↑↑n x
, where 2↑↑n
is Knuth's hyperpower notation, equal to 2 to the 2 to the ... to the 2, with n occurrences of 2.
A machine-checked proof
Actually, it's not too difficult to get the Boyer-Moore theorem prover to accept a proof of this result. Because the theorem prover uses a first-order language and twice is a higher order function, it's necessary to defunctionalize a bit, choose representations for various functions, and write an apply function. The prover also needs some guidance in finding the right inductive arguments. Here is the proof script:
; (apply f x) computes f x (defn apply (fun x) (if (equal fun 'inc) (add1 x) (if (equal fun 'id) x (if (equal fun 'twice) (cons 'twice x) (if (and (listp fun) (equal (car fun) 'twice)) ; (twice . f) represents the partial application twice f ; so (apply (twice . f) x) = f (f x) (apply (cdr fun) (apply (cdr fun) x)) nil))))) ; (funpow f n x) represents f^n x (defn funpow (fun n x) (if (zerop n) x (apply fun (funpow fun (sub1 n) x)))) ; (pow2 n) is 2^n (defn pow2 (n) (if (zerop n) 1 (times 2 (pow2 (sub1 n))))) ; f^1 x = f x (prove-lemma funpow-1 (rewrite) (equal (funpow fun 1 x) (apply fun x)) ((expand (funpow fun 1 x)))) ; f^(a+b) x = f^a (f^b x) (prove-lemma funpow-plus (rewrite) (equal (funpow fun (plus a b) x) (funpow fun a (funpow fun b x)))) (prove-lemma times-2 (rewrite) (equal (times 2 a) (plus a a))) ; This recursive function shows the inductive pattern that must be ; followed in proving the next theorem (defn induct-1 (n fun x) (if (zerop n) t (and (induct-1 (sub1 n) fun x) (induct-1 (sub1 n) fun (funpow fun (pow2 (sub1 n)) x))))) ; twice^n f x = f^(2^n) x (prove-lemma funpow-twice (rewrite) (equal (apply (funpow 'twice n fun) x) (funpow fun (pow2 n) x)) ((induct (induct-1 n fun x)))) ; (tee-n n) is the function T_n = twice twice ... twice (n occurrences) (defn tee-n (n) (if (zerop n) 'id (apply (tee-n (sub1 n)) 'twice))) ; (hyper2 n) is 2^^n a la Knuth (defn hyper2 (n) (if (zerop n) 1 (pow2 (hyper2 (sub1 n))))) ; Another function that guides an inductive proof (defn induct-2 (n fun x) (if (zerop n) t (induct-2 (sub1 n) 'twice fun))) ; T_n f x = f^(2^^n) x (prove-lemma tee-n-hyper (rewrite) (equal (apply (apply (tee-n n) fun) x) (funpow fun (hyper2 n) x)) ((induct (induct-2 n fun x))))