Fork me on GitHub

My last little foray into clojure went really well. I have another idea I could use some validation for, as well as some direction on how to approach it. I watched a Pharo Smalltalk demo and thought the codebase visualization and reflection metrics were really cool. What I'd like to do is be able to parse a clojure codebase, run some metrics on that codebase, and then generate some visualization tools and maybe even a way to traverse the codebase. Some simple ideas might be showing the number/location of references to a function. Showing a hierarchy of macro usage, along with some example inputs/outputs that show macro expansion. In general being able to generate a graph of the codebase that spans out from the entry point(s). Would this type of tool be at all useful? Is there something like this already out there? If it would be useful, what are some additional things that also might be nice to have with a tool like this? Lastly, if I were to attempt this, what would be a good way to approach it? I'd really like to be able to fully parse the contents of a file/codebase so that I can have all the semantic information I need without having to resort to hacks/tricks.


I had a similar idea/need a long time. There are some plugins/libs to visualize the call graph, e.g. clj-usage-graph:

Tim Robinson11:01:49

Can anyone explain why I would use force with a delay? We already have @ and deref and it seems to me they do exactly the same thing


force is agnostic about whether its argument is actually a delay

user=> (force 42)


and @ is already just a reader shorthand for deref

Tim Robinson17:01:12

ok thanks - I can't really think of a use case for that but I guess it was important to someone :)


Why do I get here a error :

(defn to-json[suspects]
  into {} (clojure.string/join( ", " (get :name suspects) (get :glitter-index suspects))))

(to-json {:name "Roelof" :glitter-index 2})


; Execution error (ClassCastException) at chapter4/to-json (form-init1060733672431633963.clj:82).
; class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')


because you wrote clojure.string/join(", " ...)


to call a function in clojure you must use sexp form like this (clojure.string/join ", " ...)


still no luck


(defn to-json [suspects]
  (  into {} (clojure.string/join ", " [(get :name suspects)])))

(to-json {:name "Roelof" :glitter-index 2})


; Execution error (IllegalArgumentException) at chapter4/to-json (form-init1060733672431633963.clj:82).
; Don't know how to create ISeq from: java.lang.Character

Antonio Bibiano12:01:54

get takes the map first and then the key

Antonio Bibiano12:01:14

(get suspects :name)


join will return a string calling (into {} β€œsome string”) will throw that exception


maybe you can describe what you trying to achieve?

Tim Robinson12:01:06

also [(get :name suspects)] is a vector with one entry, so string/join won't have anything to join


what I try to achieve is to convert this : {:name "John", :glitterindex: 2) into a json without using a json library


so the outcome is {"John Doe", 2}


(def foo {:name "John Doe" :glitterindex 2})


(apply hash-map (vals foo))


{"John Doe" 2}


maps in clojure is unordered so vals might return [2 "john"]


However, here be dragons, json processing is very edge casey, lots of work arounds and cavets - hence a trivial translation won't suit


I would just use a library tbh and @delaguardo is right πŸ™‚


thanks, but I still think the purpose of the challenge of the brave book was to do it manually


Then you can use juxt to extract values under certain keys from a map


juxt ?? That one is not explained in thir or a former chapter


could you share a link to your current chapter?


otherwise I can only guess what you are trying to do)


ok, so is it exercise 4 you are working on?

Antonio Bibiano13:01:38

Ah I just did that one

Antonio Bibiano13:01:52

if that's ok with you I can share my solution

Antonio Bibiano13:01:00

but I was converting to CSV


yep, the lastest ine


@antbbn of course you may. Maybe I find some ideas I can use

Antonio Bibiano15:01:21

(defn get-many
  [ks record]
  (map #(get record %) ks))
(defn suspects-to-csv 
  (let [header-keys (keys (first suspects))]
    (reduce #(clojure.string/join "\n" [%1 %2]) 
            (clojure.string/join "," header-keys)
             (comp (partial clojure.string/join ",") (partial get-many header-keys))

Antonio Bibiano15:01:51

I used a let block to make sure that I always extract the keys in the same order


get-many could just be (map record ks) if you are using normal hash maps


also, keys and vals will always return the same order for two collections with the same keys


finally your string/join calls could be str calls, if performance is a concern at all you could use a StringBuilder instead

Antonio Bibiano15:01:23

mhh I lost part of the conversation


yeah, slack is being weird


(the main reason to use join instead of iterating str calls is performance, using small calls to join in a loop like this undoes that advantage)


I guess the one gotcha is an ArrayMap vs. HashMap, which I didn't consider at first


Hi, I have a map and i want to fetch the keys which has more that 10 value, How can I get it

{"hello" 10, "hi" 12}


output should be {hi 12}


Use filter


(sorry, hi first !!)


@popeyepwr (into {} (filter #(< 10 (val %)) coll)) (if the hint to "use filter" isn't enough on its own)

βœ… 2
😁 1

Hey I did a full answer then, but making o e answer in multiple message was not a good idea with slack outage. The full answer never arrive !!

Antonio Bibiano18:01:24

another thing that I noticed looking at my code is that I reduce over the result of a mapping over the list of suspects

Antonio Bibiano18:01:56

but It could also be refactored to reduce over the suspect by using a slightly more complex function

Antonio Bibiano18:01:03

(defn suspects-to-csv 
  (let [header-keys (keys (first suspects))]
    (reduce (fn [result m]
              (str result 
                   (clojure.string/join "," (map m header-keys))))
            (clojure.string/join "," header-keys)

Antonio Bibiano18:01:28

which style would be more idiomatic?

Max Deineko19:01:23

Reducing without building intermediate sequence first can be faster -- I'd think only noticeably so with larger sequences. As a beginner I usually find separate reduce and map steps sometimes easier to manipulate in code, but that's probably highly editor/user-dependent. I'd be surprised though if one of the two variants was less idiomatic than the other -- I'd expect readability/style preference to be criterium of choice here.

Antonio Bibiano20:01:02

Makes sense, also I agree that in this context and as a beginner reducing over something that is already mapped seems more straightforward

Kannan Ramamoorthy18:01:08

I’m fiddling to setup test files and CLI commands to run it using deps.edn. If anyone could point me out to any reference that would be helpful.

Kannan Ramamoorthy18:01:33

Is there any sample projects that I can look upto? I referred to @U04V70XH6’s And added below,

:test   {:extra-paths ["src/test/clojure"]
          :extra-deps  {org.clojure/test.check {:mvn/version "RELEASE"}}}

 :runner {:extra-deps {com.cognitect/test-runner
                       {:git/url ""
                        :sha     "b6b3193fcc42659d7e46ecd1884a228993441182"}}
          :main-opts  ["-m" "cognitect.test-runner"
                       "-d" "test"
                       "-d" "src/test/clojure"]}
but when I run clj -M:test or clj -M:runner , it takes me to repl instead of running the tests.


@UMU908UNM Check clojure -Sdescribe -- I suspect you're on an old version.


Most of my projects assume or higher.


For example and both state or higher as a requirement in their READMEs (my dot-clojure does not, but it probably should).


All of my projects use deps.edn for testing these days -- is a good example since it has both GitHub Actions and CircleCI configured to run tests via deps.edn

Kannan Ramamoorthy18:01:34

My version is β€œ1.10.1.727”. But not sure why running clj -M:testΒ orΒ `clj -M:runner` takes me to repl. Will check some of your projects and compare my deps.edn. Thanks Sean!


Aren't the :test and :runner aliases meant to be run together? like so: clj -M:test:runner Not sure about :runner on its own but the :test alias doesn't have a :main-opts entry, so I think it makes sense that it takes @UMU908UNM to a repl, no? :thinking_face:


Oh, well spotted! Yes, you need -M:test:runner


But clj -M:runner should not give you a REPL -- I would expect in most projects for it to give you an exception since you won't have any test dependencies added in.

πŸ‘ 1

For example:

(! 511)-> clojure -X:new :name kannan/example
Downloading: seancorfield/clj-new/maven-metadata.xml from clojars
Generating a project called example based on the 'lib' template.
The lib template is intended for library projects, not applications.
(! 512)-> cd example/
(! 513)-> clojure -M:runner

Running tests in #{"test"}
Execution error (FileNotFoundException) at cognitect.test-runner/test (test_runner.clj:62).
Could not locate kannan/example_test__init.class, kannan/example_test.clj or kannan/example_test.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.

Full report at:


For what it's worth, here's what I'm getting in each case (this is for a project that doesn't have any tests yet):

> clj -M:test       
Clojure 1.10.1
> clj -M:runner

Running tests in #{"test" "src/test/clojure"}

Testing user

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
> clj -M:test:runner

Running tests in #{"test" "src/test/clojure"}

Testing user

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
(I'm on v1.10.1.763)


@UEQPKG7HQ Yup, it's going to depend on exactly what's in your project, with -M:test producing a REPL (since there's no :main-opts in that alias) and -M:runner either failing (as I showed above in a freshly created project) or finding no tests to run as you saw.

πŸ‘ 1
Kannan Ramamoorthy04:01:06

Got it working! Thank you Sean and Pavlos. Took a while to relaize that unlike Java, we have to have different namespace for test and actual source that we have to test. In java the package name stays the same for both the actual and test class.

Kannan Ramamoorthy04:01:55

I assume the reason being, ns is not exactly equivalent to package. But kind of equivalent to fully qualified class name.


Ah, yes! The convention is that if you have src/foo/bar.clj (with (ns ...)) then you would have test/foo/bar_test.clj (with (ns ...)).


If you create a brand new project (with clj-new) that's what you'll see...


[email protected]:~/clojure$ clojure -X:new :name
Downloading: seancorfield/clj-new/maven-metadata.xml from clojars
Generating a project called based on the 'lib' template.
The lib template is intended for library projects, not applications.
[email protected]:~/clojure$ tree
β”œβ”€β”€ LICENSE
β”œβ”€β”€ deps.edn
β”œβ”€β”€ doc
β”‚Β Β  └──
β”œβ”€β”€ pom.xml
β”œβ”€β”€ resources
β”œβ”€β”€ src
β”‚Β Β  └── foo
β”‚Β Β      └── bar.clj
└── test
    └── foo
        └── bar_test.clj

6 directories, 8 files
[email protected]:~/clojure$ cd
[email protected]:~/clojure/$ clojure -M:test:runner

Running tests in #{"test"}


FAIL in (a-test) (bar_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
[email protected]:~/clojure/$ cat test/foo/bar_test.clj
  (:require [clojure.test :refer :all]
            [ :refer :all]))

(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))
[email protected]:~/clojure/$


@UMU908UNM Hopefully that new project example above will be helpful!

πŸ‘ 1
Kannan Ramamoorthy05:01:47

Thanks a lot @U04V70XH6! That’s very informative!


Hi, my question is probably very dumb and apologies for that. I’m trying to understand how this compojure-api code works (app (-> (mock/request :get "/api/documents?id=123")) works behind the scene. I printed in the repl both app and the mock request , you can find them here. what I don’t understand is how (app mock-request) is executed. I can’t find the function that receives the request. Also is not clear to me what #Route is printed just before any dictionary. Thanks for any help in advance.


See Note: Compojure is old and slow O(n); works well with few routes, but generally prefer

πŸ™Œ 1
πŸ‘ 1

@francesco.losciale #Route{} is a print format for a record called Route


defrecord creates a type that acts like a hash map, but has its name prefixed when printing, and allows custom extension of protocol methods

πŸ™Œ 1

@antbbn thanks for showing me this

πŸ™Œ 1
Daniel Tobias20:01:07

hi all, im using cljs with reagent which produces an html and a js. My backend webserver is trying to render the page with jinja2 and i wanted to load the page with some data (it's a json object or an array in practice). How can i 'access' this data in cljs?


you can use inline js to embed the data in the page, and then access the data with cljs interop


more commonly the rendering happens in cljs and the data is fetched via AJAX from the server

Daniel Tobias23:01:36

I guess i could do ajax here but im changing the design from being written in straight js to cljs and thats how it currently works. I'll look into ajax though, thanks