Fork me on GitHub
#clojure
<
2019-04-28
>
joshkh07:04:17

i remember reading somewhere that anonymous functions end up being stored/cached in such a way that keeps them efficient when used more than once, say as an anonymous comparator. is that right?

vemv09:04:26

I also wondered this recently 🙂 https://github.com/clojure-goes-fast/clj-java-decompiler answered, e.g. Clojure is smart enough make fns once, in advance, rather than many, on-the-fly

Ivan Koz09:04:49

@U45T93RA6 in which case tho, in a loop?

Ivan Koz09:04:04

(do (println (fn [x] x)) (println (fn [x] x))) creates two different objects obviously

Ivan Koz09:04:37

you must store function instance returned by fn somewhere, be it local binding or Var or even java reference type

vemv09:04:29

here's the decompilation of

(defn sample []
  (do
    (println (fn [x] x))
    (println (fn [x] x))))
-> https://gist.github.com/vemv/bcd6bb57861531b1b6b9995744d27776

Ivan Koz10:04:24

@U45T93RA6 how you decompiled sample function, i can't make it work for some reason

Ivan Koz10:04:43

i mean how you called clj-java-decompiler exactly

Ivan Koz10:04:11

@U45T93RA6 its still created two classes for each function, instantiated and called both of them, no optimization there

Ivan Koz10:04:35

public static Object invokeStatic() {
        ((IFn)const__0.getRawRoot()).invoke(new endpoint$sample$fn__528376());
        return ((IFn)const__0.getRawRoot()).invoke(new endpoint$sample$fn__528378());
}

vemv10:04:31

> its still created two classes for each function, yeah, I don't expect Clojure to try comparing different fns searching for equality > instantiated object creation and short-lived GC are extremely cheap AFAICT the classes are created once, statically, that was my point. Perhaps a naive compiler would create the classes in runtime

vemv10:04:37

> i mean how you called clj-java-decompiler exactly plain usage, wrapped in (with-out-str), using 0.2.1

Ivan Koz10:04:23

(defn my-identity [x] x)
=> #'playsync.core/my-identity
(with-out-str (decompile (my-identity 5)))
=>
"
 // Decompiling class: cjd__init
 import clojure.lang.*;
 
 public class cjd__init

Ivan Koz10:04:49

it outputs cjd__init class

Ivan Koz10:04:16

or

(with-out-str (decompile (fn [] (my-identity 5))))
=>
"
 // Decompiling class: playsync/core$fn__13171
 package playsync;
 
 import clojure.lang.*;
 
 public final class core$fn__13171 extends AFunction

Ivan Koz10:04:22

but your version produced endpoint$sample class

Ivan Koz10:04:41

where sample is a named function i guess

Ivan Koz10:04:26

i'm missing something here

vemv10:04:19

wrap the defn call with decompile

joshkh17:04:10

thanks @U45T93RA6. just saw this thread. that's what i thought!

joshkh17:04:08

anonymous functions do not equal true when compared (of course), however they're reused under the hood

Ivan Koz18:04:55

@joshkh no, two different(unequal) functions will be used independently

Ivan Koz18:04:31

or we don't understand each-other 😃

joshkh18:04:22

hmm. i'm trying out clj-java-decompiler but i get the following when i run decompile

Execution error (ClassNotFoundException) at jdk.internal.loader.BuiltinClassLoader/loadClass (BuiltinClassLoader.java:582).
sun.misc.URLClassPath

joshkh18:04:46

okay i'll get around to installing jdk8 when i have some downtime 🙂 just reread the conversation between you and vemv and i understand now. separate objects but cheaply created and GC'ed? i'm happy to def functions when used often. i was just curious how it all worked underneath.

joshkh18:04:54

thanks for explaining

Ivan Koz18:04:34

yeah separate classes for each, but one instance per function

Ivan Koz18:04:09

(let [fx (fn [x] x)
      fy (fn [y] y)]
             (println (fx 5))
             (println (fx 5))
             (println (fy 10))
             (println (fy 10))
             (while nil
               (println (fy 10)))
public static Object invokeStatic() {
        final Object fx = new core$fn__14873$fx__14874();
        final Object fy = new core$fn__14873$fy__14876();
        ((IFn)const__0.getRawRoot()).invoke(((IFn)fx).invoke(const__1));
        ((IFn)const__0.getRawRoot()).invoke(((IFn)fx).invoke(const__1));
        ((IFn)const__0.getRawRoot()).invoke(((IFn)fy).invoke(const__2));
        ((IFn)const__0.getRawRoot()).invoke(((IFn)fy).invoke(const__2));
        while (true) {
            final Boolean true = Boolean.TRUE;
            if (true == null) {
                break;
            }
            if (true == Boolean.FALSE) {
                break;
            }
            ((IFn)const__0.getRawRoot()).invoke(((IFn)fy).invoke(const__2));
        }
        return null;
    }

Ivan Koz18:04:20

as you can see only two objects are created one per function

Ivan Koz09:04:41

@joshkh right, functions is just a jvm object, you can store and invoke it, unless you create many function instances in a loop. In fact there is no difference between anonymous function and named function. If i understand it correctly. (def fn-name (fn [x] x)) equals to (defn fn-name [x] x)

ivana09:04:23

It does not need to guess - just use macroexpand 🙂

(macroexpand '(defn fn-name [x] x))
=> (def fn-name (clojure.core/fn ([x] x)))

ivana09:04:51

and outside the loops each fn call seems to create a separate object, as shown in thread above and in another little example

(defn f [x] x)
(defn g [x] x)
(def h f)
[(= f g) (= f identity) (= f h)]
=> #'user/f
#'user/g
#'user/h
[false false true]
it seems that Clojure does not do optimization in this case, cause 1-st order objects may used as a different items

hlolli15:04:12

I'm trying to use the clojure java api (for a ticket on java app, used by java users)

IFn fn = Clojure.var("clojure.core", "fn");
IFn vector = Clojure.var("clojure.core", "vector");
fn.invoke(vector.invoke(), null);
but I get
An exception occured while executing the Java class. Parameter declaration missing

hlolli15:04:30

ah got it fn.invoke(null, null, vector.invoke(), null) the two first null's are probably the meta and optinal name.

joshkh17:04:57

@ivana @nxtk anonymous functions in loops was the reason i was asking. thanks for the clarification. 🙂

Ivan Koz17:04:19

@joshkh just create fn outside of a loop and you are set

joshkh17:04:02

yup, that's where i ended up. i was just curious from a lazy programmer's perspective.

yuhan18:04:51

Now that Clojure 1.10 supports extending protocols via metadata, is there any reason to prefer attaching metadata to generic maps instead of creating new records for sets of keys corresponding to a domain object?

borkdude18:04:24

I can imagine the latter is still more performant?

yuhan18:04:47

I assume it is, haven't done any benchmarks

yuhan18:04:23

I'm wondering what are the use-cases for extending via metadata? Is it more for one-off uses where you want to glue things together without setting up records?

yuhan18:04:07

oh right, metadata can be attached to lists and vectors as well, can't use records for those

dominicm18:04:40

very convenient for datafy and navable

4
borkdude18:04:50

yeah, that episode rocked.

williewillus19:04:48

hi, so I have this collection. for each element, I map it with a function that produces a vec. I'd like to map every element of the coll then flatten/combine all the vecs together I tried (transduce (map mapping-fn) concat coll) at first, but that gave me StackOverflowError for some reason. So now i have (transduce (map mapping-fn) (completing (fn [acc next] (into acc next))) [] coll). is there a prettier way?

seancorfield19:04:19

Sounds like you want cat?

ivana19:04:20

why produce a vectors to then concat them? (vec (mapcat .....)) does not work?

williewillus19:04:28

oh nice, mapcat is what I want. it's a transducer too

seancorfield19:04:30

@williewillus like this

(into [] (comp (map mapping-fn) cat) coll)

seancorfield19:04:31

Yeah, mapcat as a transducer will work for this case. cat is useful if you have a more complex pipeline -- or the catting doesn't come after a mapping 🙂

seancorfield19:04:56

(into [] (mapcat mapping-fn) coll)

weavejester22:04:59

Has anyone run across an error in core.async where it mysteriously tries to close a boolean?

Exception in thread "async-dispatch-49" java.lang.IllegalArgumentException: No implementation of method: :close! of protocol: #'clojure.core.async.impl.protocols/Channel found for class: java.lang.Boolean

weavejester22:04:35

I think something weird is going on, as I’m just opening a pipe between two channels I’ve created myself.

weavejester22:04:44

One of the channels has been added to a pub.

weavejester22:04:53

This seems to be enough to blow everything up.

weavejester22:04:31

Manually piping it with a go-loop works. Interesting, and bizarre…

souenzzo22:04:23

stop the repl, clean cache dirs, drink a coffee and try again?

weavejester22:04:57

Good idea 🙂

weavejester22:04:53

Stopping the REPL and cleaning the cache solved the problem. Thanks for the reminder 🙂

👍 4
squirrel 8
weavejester22:04:28

Okay, this is just crazy. Copying the source code of pipe works, but calling it as a function doesn’t.

weavejester22:04:23

Looks like it was just one of those weird bugs that crop up from time to time. Restarting the REPL and cleaning the cache fixed it.