Fork me on GitHub
#clojure
<
2019-04-29
>
hlolli00:04:05

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 https://www.youtube.com/watch?v=A81I9fwV8AY 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__4.getRawRoot()).invoke(csnd);
        ((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.

hlolli13:04:39

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 🙂

restenb10:04:37

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

restenb10:04:29

i do have several versions of both in my local repo

souenzzo11:04:44

@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

souenzzo11:04:30

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

funkrider13:04:27

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?

funkrider13:04:40

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.

funkrider13:04:41

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.

hlolli13:04:15

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

vlaaad13:04:55

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]]
  (local-context))
=> {vec__97005 [1 2 3 4]
    seq__97006 (3 4)
    first__97007 2
    a 1
    b 2
    rest (3 4)}

vlaaad13:04:35

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

funkrider13:04:16

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

funkrider13:04:59

thats perfect for me

funkrider14:04:37

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

👍 4
vlaaad14:04:47

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

vlaaad14:04:18

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

sgerguri23:04:58

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.

lilactown23:04:40

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

lilactown23:04:20

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

lilactown23:04:31

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

seancorfield23:04:48

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

👍 4
drone23:04:23

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
seancorfield23:04:54

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.

seancorfield23:04:02

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.

lilactown23:04:16

yeah I use maps for everything unless I need speed

drone23:04:37

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

lilactown23:04:43

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

ivana23:04:24

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

seancorfield23:04:27

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 🙂

drone23:04:53

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

lilactown23:04:20

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

seancorfield23:04:12

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

seancorfield23:04:36

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

lilactown23:04:03

@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

seancorfield23:04:09

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

ivana23:04:30

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