One of my favorite ways to test the power of a language I'm learning is to try and implement various fixed-point combinators. Since I'm learning Clojure (though I'm not new to lisps), I did the same for it.
First, a little "testable" code, factorial:
(def !' "un-fixed factorial function" (fn [f] (fn [n] (if (zero? n) 1 (* n (f (dec n))))))) (defn ! "factorial" [n] (if (zero? n) 1 (apply * (map inc (range n))))) For any combinator c I implement, I want to verify that ((c !') n) is equal to (! n).
We start with the traditional Y:
(defn Y "pure lazy Y combinator => stack overflow" [f] (let [A (fn [x] (f (x x)))] (A A))) But of course Clojure is not nearly so lazy as that, so we pivot to Z:
(defn Z "strict fixed-point combinator" [f] (let [A (fn [x] (f (fn [v] ((x x) v))))] (A A))) And indeed, it holds that (= ((Z !') n) (! n)).
Now comes my issue: I cannot get either of U or the Turing combinator (theta-v) to work correctly. I suspect with U it's a language limit, while with theta-v I'm more inclined to believe it's a misread of Wikipedia's notation:
(defn U "the U combinator => broken???" [f] (f f)) (defn theta-v "Turing fixed-point combinator by-value" [f] (let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))] (A A))) A sample REPL experience:
((U !') 5) ;=> Execution error (ClassCastException) at fix/!'$fn (fix.clj:55). ;=> fix$_BANG__SINGLEQUOTE_$fn__180 cannot be cast to java.lang.Number ((theta-v !') 5) ;=> Execution error (ClassCastException) at fix/theta-v$A$fn (fix.clj:36). ;=> java.lang.Long cannot be cast to clojure.lang.IFn Can anyone explain
- Why these implementations of U and theta-v are not working; and
- How to fix them?
(*)is1, you don't need1as a special case in function!.(defn ! [n] (apply * (map inc (range n))))works, as does(defn ! [n] (reduce * (range 1 (inc n)))), which I prefer.