Fork me on GitHub

I'm thinking if I've stumbled upon a bug, I've been having GC problems with Java-JNA, which seem to be because clojure hash-map assignments assign the original variable to a null. As can be seen in this short screencast I did it seems that by looking at the decompiled clojure class

public static Object invokeStatic() {
        Object csnd = ((IFn)const__0.getRawRoot()).invoke();
        Object thread = ((IFn)const__1.getRawRoot()).invoke(null);
        ((IFn)const__2.getRawRoot()).invoke((Object)new bug$spawn_csound_client$fn__8111(csnd), (Object)const__3);
        ((IFn)const__5.getRawRoot()).invoke(csnd, (Object)new bug$spawn_csound_client$fn__8113());
        Object[] arrobject = new Object[10];
        arrobject[0] = const__6;
        arrobject[1] = csnd;
        arrobject[2] = const__7;
        Object object = thread;
        thread = null;
        arrobject[3] = new bug$spawn_csound_client$fn__8115(csnd, object);
        arrobject[4] = const__8;
        arrobject[5] = new bug$spawn_csound_client$fn__8120(csnd);
        arrobject[6] = const__9;
        arrobject[7] = new bug$spawn_csound_client$fn__8123(csnd);
        arrobject[8] = const__10;
        Object object2 = csnd;
        csnd = null;
        arrobject[9] = new bug$spawn_csound_client$fn__8125(object2);
        return RT.mapUniqueKeys((Object[])arrobject);
that this csnd = null; defenitely shouldn't be there? Now knowing this, I can prevent this, but it's one of those annoying bugs 🙂

🐛 4
Alex Miller (Clojure team)01:04:35

I wouldn’t trust the decompiler, look at the actual bytecode. Clojure adds bytecode that clears the passed args to prevent head-holding. This is not representable in Java so the decompilation will typically look weird.


As I used VisualVM, I was able to track down this GC error, it comes from the body.

(set-message-callback csnd (fn [attr msg] (print msg)))
refering to
(defn set-message-callback [^Csound instance callback]
  (let [msg-cb ^MessageCallback
        (reify MessageCallback
          (invoke [this inst
                   attr msg]
            (callback attr msg)))]
    (.setMessageCallback instance msg-cb)))
So nothing to do with this line that I commented in the video 🙂


trying to upgrade an older app from clojure 1.8.0 to 1.9.0, but running into missing dependencies on what I believe is spec.alpha and core.specs.alpha. basically :cause Could not locate clojure/spec__init.class or clojure/spec.clj on classpath. on build


i do have several versions of both in my local repo


@restenb some dependency from your project need to be upgraded too. clojure.spec.alpha was first released(as alpha) as clojure.spec then renamed to clojure.spec.alpha


probably clojure.future.spec

Alex Miller (Clojure team)12:04:43

Clojure 1.9 depends on spec.alpha so shouldn’t need future spec

Alex Miller (Clojure team)12:04:18

You should get that automatically though with lein, etc


Morning Clojurists. I am wondering if it is possible to retrieve the value of a let bound symbol using its string name?

(let [e :a] (some-fn "e"))

Alex Miller (Clojure team)13:04:34

that would be an unusual thing to do

Alex Miller (Clojure team)13:04:18

possible via macro invocation, but why?


Urg I am afraid its a long story involving a vector of keywords and symbols used with core match and some code I am trying to write tests for.


Is suppose what I don't quite understand is where your symbols are defined when you use a let, and if there is a way to enumerate whats available in your context. I see ns-publics but that doesn't include let defined stuff.


@ian.davies maybe you want to represent your data differently, maybe (let [e {:value :a :from "e"}]) or something


And here is a macro to get local bindings:

(defmacro local-context []
  (let [symbols (keys &env)]
    (zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))

(let [[a b & rest] [1 2 3 4]]
=> {vec__97005 [1 2 3 4]
    seq__97006 (3 4)
    first__97007 2
    a 1
    b 2
    rest (3 4)}


(keys in a map are symbols, not keywords in this case)


so &env is a map that contains the symbols from a let - but available from within a macro


thats perfect for me


thank you @vlaad, @alexmiller and @hlolli for taking time to answer my question - your help is much appreciated 😉

👍 4

it contains all available local bindings, not only from let, but also from loop, fn etc.


((fn [a]
   (loop [b 1]
     (let [c 3]
       (local-context)))) 0)
=> {a 0, b 1, c 3}


They're helpful as wrappers for stateful connections for isolating interfaces. An example would be a DB connection pool, wrapped in a record with a single field, implementing a high-level DB API in the form of a Protocol. I use maps for regular and domain data, and write specs for those.


I think the biggest thing is protocols are quite a bit faster than a map lookup


so for paths that are hot, but still need the kind of dynamicity of maps, records can help optimize those paths without losing semantics of a map


I think I remember someone saying on here that they were first introduced when exploring implementing the Clojure compiler in Clojure


@deleted-user Bottom line: use maps unless you have a good reason to use records 🙂

👍 4

I’m a fan of records because of the readability, generated code (e.g., creation fns), and protocol support. I use them all over, including for domain data. I use maps for intermediate or ad-hoc data. But you’re right in noticing that records have been de-emphasized and that’s only continuing

👍 4

Alex (Miller) has commented (several times, in several forums) that his book, Clojure Applied, used records a lot more heavily than he would do these days -- so, yes, definitely a de-emphasis over time. Part of the issue is that records don't support namespace-qualified keys and therefore don't really play nice with clojure.spec.


Another issue that I've run into is that if you accidentally dissoc one of the record basis fields, it changes type to a map silently which can cause all sorts of weird, hard-to-debug breakage. So you have some fields you can dissoc and some that you can only assoc to nil instead.


yeah I use maps for everything unless I need speed


ew, didn’t know about the dissocing of a record field


esp. now that you can extend-via-metadata, it's essentially a tradeoff of: namespaced keywords vs. speed


Sometimes I want to have a criteria for splitting some values by type. It can be done by regular maps with special :type keys (bad solution), maps with metadata (better, but have some issues, but I choose this way often) and records - I think is it a good solution to add any type/meta labels to data


My two uses of records are: where I need something map-like to participate in a protocol or where I need something map-like and speed is really important -- and I don't need namespace-qualified keys 🙂


wait, so extend-via-metadata does requires runtime dispatch I guess?


it has to lookup metadata, then lookup the symbol in the metadata


That's why it is opt in -- protocols have to declare that they can be extended to arbitrary objects via metadata.


datafy/`nav` in Clojure 1.10 rely heavily on that.


@ivana i try really hard to namespace my keywords and let them quack appropriately 🦆 by the time I want to add a :type key I start looking for a different way of modeling the information


I noticed that the Component library also got an update recently to opt in to extension via metadata, which is really nice.


@lilactown yes, and types/records seems to be a good way of such different way of modeling I think