Skip to content

Instantly share code, notes, and snippets.

@shilder
Created August 15, 2018 15:49
Show Gist options
  • Save shilder/7b2412e76f40ba533166526a1132cd4e to your computer and use it in GitHub Desktop.
Save shilder/7b2412e76f40ba533166526a1132cd4e to your computer and use it in GitHub Desktop.
clojure fn caching and deftype issues
(ns repl.pitfalls
(:require [clojure.core.async :as async])
(:import (java.util.concurrent DelayQueue Delayed TimeUnit)))
; some interesting repl behaviour
; this are not bugs, but just some counterintuitive behaviour
; all of the described issues are caused by some state (thread/queue/defonce)
; fn-caching
(defn foo [a]
(println "Hello" a))
(defonce async-thread
(async/thread
; foo is dereferenced and cached here, changing foo will not
; change calls of partial
(let [f (partial foo 1)]
(loop []
; it will always print "Hello 1"
(f)
(Thread/sleep 2000)
(recur)))))
; little delay to prevent overlapping of output
(Thread/sleep 100)
(defonce async-thread2
(async/thread
; there are 2 workarounds for this:
; 1. Use lambda and call required function - it will be dereferenced on every call
; 2. Use `var` special form (reader macro #')
(let [f1 #(foo 2)
; these 2 are the same
f2 (partial (var foo) 2)
f3 (partial #'foo 2)]
(loop []
; This will change when foo is redefined
(f3)
(Thread/sleep 2000)
(recur)))))
(foo "Old")
; this definition will overwrite old `foo` for new calls, but in thread
; it still uses old version
(defn foo [a]
(println "Goodbye" a))
(foo "New")
;; Deftype and storing objects
(defonce delay-queue (DelayQueue.))
; every evaluation of deftype creates new class
(deftype MyNewDelay [endtime]
Delayed
(getDelay [this time-unit]
(.convert time-unit
(- endtime (System/currentTimeMillis))
TimeUnit/MILLISECONDS))
(compareTo
[this other]
(let [oend (.-endtime ^MyNewDelay other)]
(if (< endtime oend)
-1
(if (= endtime oend)
0
1)))))
(defonce object (->MyNewDelay 1))
; on first load it will output true, on subsequent reloads it will output false
(println (= (class object)
(class (->MyNewDelay 1))))
; You can't load this file twice it will cause CompilerException
; java.lang.ClassCastException:
; repl.pitfalls.MyNewDelay cannot be cast to repl.pitfalls.MyNewDelay
;
; this is because deftype generates new class every time it's evaluated
; and we have 'old' object in queue. So class names/bodyes are the same, but
; classes are not (from ClassLoader point of view)
(.put delay-queue (->MyNewDelay (+ (System/currentTimeMillis) 10000)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment