Time Travel With Clojure

Atamert Ölçgen

March 2018 - Singapore Clojure Group

What is this talk about?

I want this to happen, right here and right now.

What is this talk not about?


Example 1: Overcoming resource limitations

Search a unique file in a large file system.

We do not have infinite memory.

There is no index.

		{:name "/"
		 :children [
		   {:name "foo/"
		    :children-fn (fn [...] ...)}
		   {:name "bar/"
		    :children-fn (fn [...] ...)}
		   {:name "baz/"
		    :children-fn (fn [...] ...)}

		(defn get-children [node]
		  (assoc node :children ((:children-fn node) node)))

		(defn forget-children [node]
		  (dissoc node :children))

What if we did not have a memory constraint?

		{:name "/"
		 :children [
		   {:name "foo/"
		    :children (delay ...)}
		   {:name "bar/"
		    :children (delay ...)}
		   {:name "baz/"
		    :children (delay ...)}

		(defn get-children [node]
		  @(:children node))

Lazy Sequence

		  (defn numbers
		    ([] (numbers 1))
		    ([i] [i (delay (numbers (inc i)))]))

		  (defn head [sequence] (first sequence))

		  (defn tail [sequence] @(second sequence))

Lazy Sequence - Clojure Way

		  (defn numbers
		    ([] (numbers 1))
		    ([i] (lazy-seq (cons i (numbers (inc i))))))

		  (first (numbers))    # => 1
		  (take 10 (numbers))  # => (1 2 3 4 5 6 7 8 9 10)

Example 2: Code as data

AJAX calls without core.async


		(defn handler [response]
		  (.log js/console (str response)))

		(defn error-handler [{:keys [status status-text]}]
		  (.log js/console
		        (str "something bad happened: "
		             " "

		(GET "/hello" {:handler handler
		               :error-handler error-handler})


Composable Futures

		(defmacro and-then [f g]
		    (let [result# (deref ~f)]
		      (~g result#))))

		(-> (foo)            ;; foo returns a future
		    (and-then bar)   ;; bar returns a value
		    (and-then baz))  ;; baz returns a value

		`["my.ns/foo" -> "ns.two/bar" -> "ns.three/baz"]

Programs Writing Programs

Time travel Exploratory Programming

Let's take it down a notch.

Object Oriented Programming

		(def rect
		  {:width 100
		   :height 400
		   :x 50
		   :y 50
		   :area (fn [self]
		             (* (:width self) (:height self)))
		   :circumference (fn [self]
		                      (* (+ (:width self)
		                            (:height self))

		(defn circumference [obj]
		  ((:circumference obj) obj))

SICP, dispatching on type

Object Oriented Programming

Can we serialize our object?

		(pr-str (:area rect))
		;; => "#object[user$fn__1287
		;;             0x62709976
		;;             \"user$fn__1287@62709976\"]"

Serializing Functions

		(defmacro sfn1 [param & body]
		  (let [func-data `(fn [~param] ~@body)]
		    `{:fn ~func-data
		      :fn-data '~func-data}))

		(def area-of-rect
		  (sfn1 self
		        (* (:width self) (:height self))))

		;; => {:fn #object[user$fn__1327
		;;                 0x14cfc66e
		;;                 "user$fn__1327@14cfc66e"],
		;;     :fn-data (clojure.core/fn [self]
		;;                (* (:width self) (:height self)))}


Q: How can we defer execution of a form?

A: quote, '(...), `(...)

Q: How can we control execution of forms at read-time?

A: Macros.



Appendix: Images