This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-04
Channels
- # announcements (42)
- # architecture (18)
- # aws (30)
- # beginners (119)
- # calva (1)
- # cider (3)
- # cljs-dev (15)
- # cljsrn (16)
- # clojure (80)
- # clojure-australia (3)
- # clojure-europe (11)
- # clojure-germany (2)
- # clojure-nl (7)
- # clojure-sanfrancisco (4)
- # clojure-spec (12)
- # clojure-taiwan (2)
- # clojure-uk (10)
- # clojurescript (22)
- # events (2)
- # fulcro (31)
- # ghostwheel (3)
- # jobs (1)
- # kaocha (1)
- # luminus (22)
- # meander (1)
- # off-topic (14)
- # pathom (5)
- # portal (10)
- # re-frame (12)
- # reitit (1)
- # shadow-cljs (19)
- # spacemacs (11)
- # tools-deps (34)
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: https://github.com/gfredericks/clj-usage-graph/blob/master/src/com/gfredericks/clj_usage_graph/usages.clj
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)
42
and @
is already just a reader shorthand for deref
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 ", " ...)
(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
get takes the map first and then the key
(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?
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
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
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?
Ah I just did that one
if that's ok with you I can share my solution
but I was converting to CSV
(defn get-many
[ks record]
(map #(get record %) ks))
(defn suspects-to-csv
[suspects]
(let [header-keys (keys (first suspects))]
(reduce #(clojure.string/join "\n" [%1 %2])
(clojure.string/join "," header-keys)
(map
(comp (partial clojure.string/join ",") (partial get-many header-keys))
suspects))))
I used a let block to make sure that I always extract the keys in the same order
using get-many
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
mhh I lost part of the conversation
yeah, slack is being weird
but now that I think about it totally makes sense that keys and vals will keep the order for the same collection
The main reason why I used get-many (or (map record ks)
as you suggested) is because I was not sure if vals
will be in the same order for different maps with the same keys
as far as I understand that is not guaranteed
Maps in clojure are not ordered. For ordered maps see https://github.com/flatland/ordered
PersistentArrayMap is, PersistentHashMap isn't, and past 8 pairs the former is "upgraded" to the latter
so you think it's a good idea to make sure that you get the keys in the explicit order you want using something like (map record ks)
Although the order of keys
(or vals
) is "random", they are consistent: whatever order keys
gives you matches whatever order vals
gives you.
gotcha, it's only that the gist of that exercise was to convert that list of hash maps to a csv
and given that each map is a different element of the list even though they have the same keys
I was worried about the case where vals
of one item might return them in a different order than vals
of another item
Right, I think your overall approach -- get keys
from the first hash map and use that, in order, to get values from the other maps makes sense. You can't rely on vals
producing the same order of keys across multiple hash maps.
Nice! thanks for the tips
@UCCHXTXV4 maps are not ordered, but to PersistentHashMap instances with the same keys will have a keys
output and a vals
output in the same order
I didn't say it was orderd, I said vals
or keys
on two PersistentHashMap with the same keyset have the same order
(ins)user=> (def m1 (into {} [[:a 0] [:b 1] [:c 2] [:d 3]]))
#'user/m1
(ins)user=> (def m2 (into {} [[:a "a"] [:b "b"] [:c "c"] [:d "d"]]))
#'user/m2
(ins)user=> (= (keys m1) (keys m2))
true
@U051SS2EU Not quite. keys
and vals
are only guaranteed to return orders consistent with each other for the same identical map object. While it is common for two PersistentHashMap
objects that are =
to return the same order as each other for keys
(or vals
), that is not guaranteed, and can be false when two keys have the same hash value, but were added to the not-identical-but-= PersistentHashMap
instances.
Example:
$ clj
Clojure 1.10.1
(clojure-version)
"1.10.1"
(hash 0)
0
(hash 92612215)
0
(def m1 (-> {} (assoc 0 :added-first) (assoc 92612215 :added-second)))
#'user/m1
(def m2 (-> {} (assoc 92612215 :added-first) (assoc 0 :added-second)))
#'user/m2
m1
{0 :added-first, 92612215 :added-second}
m2
{92612215 :added-first, 0 :added-second}
(= m1 m2)
false
(keys m1)
(0 92612215)
(keys m2)
(92612215 0)
This happens because PersistentHashMap
puts all keys with the same hash
value into the same "hash bucket", which is simply a linked list of elements, in the order that they were last added to the PersistentHashMap
thanks for the info
In Andy's example, the PersistentArrayMap
is used so it preserves insertion order but the same holds for PersistentHashMap
:
(def m1h (-> (hash-map :very-first -1) (assoc 0 :added-first) (assoc 92612215 :added-second)))
(keys m1h)
;; => (0 92612215 :very-first)
(def m2h (-> (hash-map :very-first -1) (assoc 92612215 :added-first) (assoc 0 :added-second)))
(keys m2h)
;; => (92612215 0 :very-first)
Ah, yes, sorry I didn't force the use of PersistentHashMap, but the example I had written up earlier did do so, as jumar's example does.
(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}
@popeyepwr (into {} (filter #(< 10 (val %)) coll))
(if the hint to "use filter" isn't enough on its own)
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 !!
The main reason why I used get-many (or (map record ks)
as you suggested) is because I was not sure if vals
will be in the same order for different maps with the same keys
as far as I understand that is not guaranteed
another thing that I noticed looking at my code is that I reduce over the result of a mapping over the list of suspects
but It could also be refactored to reduce over the suspect by using a slightly more complex function
like so
(defn suspects-to-csv
[suspects]
(let [header-keys (keys (first suspects))]
(reduce (fn [result m]
(str result
"\n"
(clojure.string/join "," (map m header-keys))))
(clojure.string/join "," header-keys)
suspects)))
which style would be more idiomatic?
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.
Makes sense, also I agree that in this context and as a beginner reducing over something that is already mapped seems more straightforward
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.
Is there any sample projects that I can look upto? I referred to @U04V70XH6βs https://github.com/seancorfield/dot-clojure/blob/develop/deps.edn. 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 1.10.1.727 or higher.
For example https://github.com/seancorfield/clj-new and https://github.com/seancorfield/depstar both state 1.10.1.727 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 -- https://github.com/seancorfield/next-jdbc/ is a good example since it has both GitHub Actions and CircleCI configured to run tests via deps.edn
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.
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:
/var/folders/p1/30gnjddx6p193frh670pl8nh0000gn/T/clojure-3814224161163105319.edn
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
user=>
> 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.
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.
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 foo.bar ...)
) then you would have test/foo/bar_test.clj
(with (ns foo.bar-test ...)
).
If you create a brand new project (with clj-new
) that's what you'll see...
seanc@DESKTOP-30ICA76:~/clojure$ clojure -X:new :name foo.bar
Downloading: seancorfield/clj-new/maven-metadata.xml from clojars
Generating a project called foo.bar based on the 'lib' template.
The lib template is intended for library projects, not applications.
seanc@DESKTOP-30ICA76:~/clojure$ tree foo.bar
foo.bar
βββ CHANGELOG.md
βββ LICENSE
βββ README.md
βββ deps.edn
βββ doc
βΒ Β βββ intro.md
βββ pom.xml
βββ resources
βββ src
βΒ Β βββ foo
βΒ Β βββ bar.clj
βββ test
βββ foo
βββ bar_test.clj
6 directories, 8 files
seanc@DESKTOP-30ICA76:~/clojure$ cd foo.bar/
seanc@DESKTOP-30ICA76:~/clojure/foo.bar$ clojure -M:test:runner
Running tests in #{"test"}
Testing foo.bar-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.
seanc@DESKTOP-30ICA76:~/clojure/foo.bar$ cat test/foo/bar_test.clj
(ns foo.bar-test
(:require [clojure.test :refer :all]
[foo.bar :refer :all]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 1))))
seanc@DESKTOP-30ICA76:~/clojure/foo.bar$
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. https://paste.ofcode.org/gYrXDmzUzHTiYrQvKSzsxD 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 https://skillsmatter.com/skillscasts/3445-functional-web. Note: Compojure is old and slow O(n); works well with few routes, but generally prefer https://github.com/metosin/reitit.
@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
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
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