code-reviews

2022-01-14T06:04:28.011900Z

Oh ma gad. Got it work. For posterity: https://github.com/stopachka/simple-lisp/blob/main/src/simple_lisp/core.clj#L98-L110

2022-01-22T16:13:25.020200Z

Yes :)

Drew Verlee 2022-01-22T16:32:26.020400Z

☯️

❤️ 1
Drew Verlee 2022-01-22T04:56:42.019800Z

Is this lisp written in lisp?

vemv 2022-01-14T14:03:33.014Z

Another topic, I found myself writing this to sort by multiple criteria at once: (emphasis on str + a number that will force a certain priority in the sorting)

(sort-by (fn [[entry {:keys [lib-name path-key]}]]
           (cond
             (and path-key (original-paths-set entry))
             (str "0" entry)

             path-key
             (str "1" entry)

             (original-deps-set lib-name)
             (str "2" lib-name)

             (not (-> lib-name str (.contains "$")))
             (str "3" lib-name)

             true
             (str "4" lib-name))))
It works, but I wonder if there's something more efficient than creating strings, I don't like much the idea of creating garbage :)

Ben Sless 2022-01-14T15:02:45.014100Z

Write a custom comparator?

2022-01-14T15:04:21.014300Z

(sort-by (juxt :a :b) [{:a 3 :b 1}
                       {:a 1 :b 1}
                       {:a 1 :b 2}])

=> ({:a 1, :b 1} {:a 1, :b 2} {:a 3, :b 1})

Ben Sless 2022-01-14T15:08:49.014500Z

Juxt allocates a tuple for the result

👀 1
vemv 2022-01-14T15:14:17.014800Z

yeah was wondering how convoluted the comparator would look like. I haven't dived into it but perhaps the more criteria you add, the more convoluted it would become

Ben Sless 2022-01-14T16:26:53.015Z

You could write a function which takes a hierarchy of predicates a d returns a comparator, or something similar

👀 1
2022-01-14T17:11:56.015300Z

you can also just construct a vector instead of a string [0 entry] [1 entry] [2 lib-name] etc as long as your algorithm ensures an entry and lib-name are never compared against one another

2022-01-14T17:12:37.015500Z

my custom comparators often end up creating vectors for the comparison (not free, but cheaper than those strings allocation wise)

👀 1
2022-01-14T17:13:04.015700Z

this is clojure, every function call represents multiple allocations

😱 1
vemv 2022-01-14T17:16:54.016100Z

> this is clojure, every function call represents multiple allocations I wouldn't have imagined that. Even invoking an empty or near-empty defn (`(defn foo [] 42)`) represents an alloc?

2022-01-14T17:19:25.016300Z

the arg list is an alloc, for example (perhaps there's an optimization if the list is empty)

2022-01-14T17:20:16.016500Z

remember that the jvm doesn't store any data structures on the stack, if it's not an immediate primitive, it's a heap allocation

2022-01-14T17:21:18.016700Z

I'd have to go read code to get the specifics, but even for just entering the scope of the function, I'd assume there's an alloc or two...

2022-01-14T17:23:42.017Z

unless foo is a local that was captured (in which case we need to analyze the parent that captured it...) even just calling foo will cause a var lookup from the ns at the very least, and I don't think that's garbage free in clojure

vemv 2022-01-14T17:24:27.017300Z

Great insights thanks!

2022-01-14T17:25:39.017500Z

to get the real details, check out the stack traces you see with simple function calls, and ideally find a good disassembler (|'ve been meaning to check what the state of the art is these days... no.disassemble was nice but inconvenient)

2022-01-14T17:30:52.017700Z

(ins)user=> (decompile (fn [] (foo)))

// Decompiling class: user$fn_line_1__212
import clojure.lang.*;

public final class user$fn_line_1__212 extends AFunction
{
    public static final Var const__0;
    
    public static Object invokeStatic() {
        return ((IFn)user$fn_line_1__212.const__0.getRawRoot()).invoke();
    }
    
    @Override
    public Object invoke() {
        return invokeStatic();
    }
    
    static {
        const__0 = RT.var("user", "foo");
    }
}

nil

2022-01-14T17:31:04.017900Z

that's using https://github.com/clojure-goes-fast/clj-java-decompiler

2022-01-14T17:32:26.018200Z

so the allocation and var lookup is being done statically - better perf wise than I first imagined

Ben Sless 2022-01-14T20:44:27.018600Z

(fn [a b c] ,,,) is compiled to a method invoke(Object a, Object b, Object c) (fn [& args]) will alloc an ArraySeq and call a different method

Ben Sless 2022-01-14T20:45:07.018800Z

the var lookup via .getRawRoot depends on direct linking, which is off by default

✔️ 1
2022-01-14T23:04:43.019500Z

What an interesting thread. Thanks for that @noisesmith !