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?

Primitives

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

Callbacks!!!!11


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

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

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

cljs-ajax

Composable Futures


		(defmacro and-then [f g]
		  `(future
		    (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))
		                         2))})
	    

		(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)))}
	    

Quiz

Q: How can we defer execution of a form?

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

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

A: Macros.

Thanks!

muhuk.github.io/clojure-time-travel

Appendix: Images