clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Yehonathan Sharvit 2021-04-22T02:39:50.420100Z

Thank you Andy!

craftybones 2021-04-22T06:44:34.420800Z

Hello, @magnars and I are working on an issue I recently found on stasis. https://github.com/magnars/stasis/issues/30

craftybones 2021-04-22T06:45:00.421600Z

Does getting class path entries differ between using Clojure as a repl and a lein repl?

craftybones 2021-04-22T06:46:03.422100Z

It might simply be the approach to getting the classpath entries

craftybones 2021-04-22T06:46:27.422400Z

the code used is:

(defn class-path-elements []
  (->> (s/split (System/getProperty "java.class.path" ".") #":")
       (remove (fn [#^String s] (.contains s "/.m2/")))))

craftybones 2021-04-22T06:47:36.423300Z

Clearly lein does stuff since it launches differently and loads libs etc

seancorfield 2021-04-22T07:02:01.424200Z

@srijayanth The local Maven repo can be in a different directory, at least with the Clojure CLI / deps.edn.

seancorfield 2021-04-22T07:02:35.424900Z

Also, the separator is different on Windows than macOS/Linux

craftybones 2021-04-22T07:03:19.426Z

@seancorfield - I think the local Maven repo being in a different directory is certainly one part of it

craftybones 2021-04-22T07:03:30.426500Z

however, it doesn’t respect a few other things too

seancorfield 2021-04-22T07:03:33.426800Z

(System/getProperty "path.separator") is what you need for the separator. It isn’t : on Windows.

craftybones 2021-04-22T07:04:35.427500Z

for instance, my deps.edn has

{:paths ["src" "examples"]}

craftybones 2021-04-22T07:05:14.428300Z

But when I run the class-path-elements fn on a plain old clojure repl, I don’t see the “examples” dir listed at all

seancorfield 2021-04-22T07:06:21.428800Z

How are you starting the REPL?

craftybones 2021-04-22T07:07:40.429200Z

clojure

craftybones 2021-04-22T07:07:44.429400Z

just plain old clojure

seancorfield 2021-04-22T07:08:37.430500Z

(! 1316)-> clojure 
Downloading: org/clojure/clojure/1.10.3/clojure-1.10.3.pom from central
Downloading: org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.pom from central
Downloading: org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.pom from central
Downloading: org/clojure/pom.contrib/0.3.0/pom.contrib-0.3.0.pom from central
Downloading: org/clojure/pom.contrib/0.3.0/pom.contrib-0.3.0.pom from central
Downloading: org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.jar from central
Downloading: org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.jar from central
Downloading: org/clojure/clojure/1.10.3/clojure-1.10.3.jar from central
Clojure 1.10.3
user=> (System/getProperty "java.class.path")
"src:examples:/Users/sean/clojure/r/libraries/org/clojure/clojure/1.10.3/clojure-1.10.3.jar:/Users/sean/clojure/r/libraries/org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.jar:/Users/sean/clojure/r/libraries/org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.jar"
user=> ^D

Thu Apr 22 00:07:54
(sean)-(jobs:0)-(~/clojure/r)
(! 1317)-> cat deps.edn 
{:mvn/local-repo "libraries"
 :paths ["src" "examples"]}
See that deps are downloaded into the local libraries folder as a Maven repo.

seancorfield 2021-04-22T07:08:48.430800Z

Also see that examples is on the class path.

craftybones 2021-04-22T07:08:58.431Z

Interesting

dpsutton 2021-04-22T07:12:35.432400Z

> Does getting class path entries differ between using Clojure as a repl and a lein repl? I updated that issue with the extra classpath roots that lein includes by default: test, target, dev-resource, etc

dpsutton 2021-04-22T07:13:02.432800Z

so the answer to that question is kinda

dpsutton 2021-04-22T07:14:31.433900Z

> Downloading: org/clojure/clojure/1.10.3/clojure-1.10.3.jar from central @seancorfield how in the world did you not have clojure 1.10.3 on your computer already. I figured you'd have had the alphas and the release on day 1

craftybones 2021-04-22T07:18:46.434100Z

Thanks @dpsutton and @srijayanth

craftybones 2021-04-22T07:19:16.434800Z

The core of the problem in that issue seems to be that under lein, the class path entries are listed as full paths, whereas when run as just Clojure, it uses a relative path

craftybones 2021-04-22T07:20:35.436200Z

@magnars assumed the way it works under lein would be the right way, not a bad assumption, but turns out that this doesn’t quite work that way. But thanks for your help, I’ve figured out how to deal with it

craftybones 2021-04-22T07:20:35.436300Z

thanks

craftybones 2021-04-22T07:21:02.436900Z

I sometimes wonder if @seancorfield is some sort of a hyperintelligent chat bot. I’ve no idea how he responds to every query

🙌 1
seancorfield 2021-04-22T07:28:56.437200Z

We’re already on 1.11.0-alpha1 in production 🙂

seancorfield 2021-04-22T07:29:56.437500Z

I don’t sleep very well, and between my macOS desktop, my Windows/WSL2 laptop, and my Android phone, I’m online pretty much the entire time I’m not actually asleep 🙂

Yehonathan Sharvit 2021-04-22T10:18:06.439900Z

A question related to lazy sequences: Is it possible to count the number of elements in a lazy sequence that doesn’t fit in a program memory? For instance a sequence of 1e6 elements where each element is 1MB. Not sure if the question is well formulated.

borkdude 2021-04-22T10:23:30.440400Z

not possible since sequences are linked lists (conceptually) and to count them you have to realize all the elements, traversing them from left to right

p-himik 2021-04-22T10:24:05.441Z

What if you keep calling next to get rid of the head?

thheller 2021-04-22T10:24:07.441200Z

but you don't have to hold on to the elements

borkdude 2021-04-22T10:24:49.441800Z

if you don't want to hold on to the head, then yes, that is possible, but this is quite an expensive operation to count items if that's all you do with them

borkdude 2021-04-22T10:25:13.442Z

there is probably a better solution

Yehonathan Sharvit 2021-04-22T10:26:24.442400Z

I need to count the rows on a HBase/Bigtable table

Yehonathan Sharvit 2021-04-22T10:26:48.443Z

As far as I know, it’s not possible to do it in the server side

Yehonathan Sharvit 2021-04-22T10:27:18.443500Z

One has to count all the row keys

Yehonathan Sharvit 2021-04-22T10:28:02.444200Z

cbass (a HBase Clojure lib) provides an API that returns a lazy sequence of rows

Yehonathan Sharvit 2021-04-22T10:28:38.445Z

I want to know what to expect in terms of memory when I count the rows

borkdude 2021-04-22T10:35:44.445500Z

@viebel The cbt command line seems to have a count option so maybe it's possible after all somehow?

restenb 2021-04-22T10:40:05.447500Z

is there a point to providing chanwith a buffer size, if I have no idea how many things will have to be on it?

Yehonathan Sharvit 2021-04-22T10:40:07.447700Z

No @borkdude. cbt scan rows and count

Yehonathan Sharvit 2021-04-22T10:40:51.448300Z

Let’s continue the discussion on a thread

restenb 2021-04-22T10:41:59.448800Z

hm. i guess unbuffered channels are meant for information exchange only, not actual queues

jumar 2021-04-22T10:44:06.448900Z

What is it again exactly what you need to do? Just count the elements?

Yehonathan Sharvit 2021-04-22T10:48:42.449100Z

Yes

Yehonathan Sharvit 2021-04-22T10:49:35.449300Z

I think that I got the answer with the help of babashks (thanks @borkdude) It takes a while but doesn’t explode the memory:

bb -e "(count (map (fn [x] (into [] (range 1e3))) (range 1e6)))"

Yehonathan Sharvit 2021-04-22T10:49:51.449500Z

while this one explodes the memory:

bb -e "(count (mapv (fn [x] (into [] (range 1e3))) (range 1e6)))"

2021-04-22T10:59:56.449700Z

(reduce #(inc % #_ %2) 0 (range 1e6)) as a possible alternative

Yehonathan Sharvit 2021-04-22T11:04:23.449900Z

Why would it be better than count @delaguardo?

2021-04-22T11:06:11.450100Z

it is the same from memory consumption or performance perspectives but can be composed with other transducers like filter

2021-04-22T11:08:22.450300Z

also I remember cli tool hbase has a rowcounter command

usage: hbase rowcounter <tablename> [options] [<column1> <column2>...]
Options:
    --starttime=<arg>       starting time filter to start counting rows from.
    --endtime=<arg>         end time filter limit, to only count rows up to this timestamp.
    --range=<arg>           [startKey],[endKey][;[startKey],[endKey]...]]
    --expectedCount=<arg>   expected number of rows to be count.
For performance, consider the following configuration properties:
-Dhbase.client.scanner.caching=100
-Dmapreduce.map.speculative=false

2021-04-22T11:08:41.450500Z

not sure if it help in your case though

Yehonathan Sharvit 2021-04-22T11:13:20.450700Z

Thank you @delaguardo

Yehonathan Sharvit 2021-04-22T11:13:39.450900Z

I am pretty sure that hbase CLI does the counting on the client side

thiru 2021-04-22T20:06:19.455100Z

With leiningen the library author's version would usually be in the defproject of project.clj. What are people typically doing for tools.deps projects? I guess putting it in deps.edn doesn't make sense?

seancorfield 2021-04-22T20:21:13.456Z

I keep it in pom.xml and update it with depstar when I build a library JAR. My deployment process is documented in the depstar README.

borkdude 2021-04-22T20:39:33.456700Z

@thiru0130 You are allowed to put data in deps.edn, but don't use unqualified keywords on the top-level since they may conflict with tools.deps in the future.

borkdude 2021-04-22T20:41:17.457900Z

having said that, I more or less adopted a convention of putting a file in resources/MY_LIB_VERSION so users can do (io/resource "MY_LIB_VERSION") and I can also use it in scripts. Not saying this is a good convention. I was already doing this in lein-managed libs.

1
thiru 2021-04-22T20:58:25.458400Z

Cool, thanks for the tips guys

Eric Auld 2021-04-22T23:48:30.478200Z

Hi, y’all. Have a question about some code I’m writing. The problem I was given as a sort of kata was to take two Clojure maps (`m1` and `m2` representing “the same” map at two different times) and find the “minimal transaction” taking `m1` to `m2`. I’m going to restrict “minimal transaction” to mean “using only simple ‘add’ and ‘remove’ commands”, since this is sort of a baby version of doing the same thing for a Datascript database, where those really are the only two things you can do. I thought it would be nice if it were fairly easy to go from `(minimal-transaction m1 m2) -> (m1 |-> m2)` …in other words, if I represented the minimal transaction in such a way that it could be easily converted into something executable. And then when that executable thing were called on `m1`, it would would produce `m2`. (Like a list of Datascript transactions can easily have datascript/q called on it, and update the database from old state to new state.) https://www.codepile.net/pile/Z1KREX8b was a bit hacky; it produces things like

example-map1
=> {:a 1, :b 2, :c {:d 3, :e #{7 6 9 8}, :f 10}}
example-map2
=> {:a 1, :b 11, :c {:d 3, :e #{13 6 12 8}, :g 14}}
(minimal-transactions example-map1 example-map2)
=>
((update-in [:b] dissoc)
 (update-in [:c :f] dissoc)
 (update-in [:c :e] disj 7)
 (update-in [:c :e] disj 9)
 (assoc-in [:b] 11)
 (update-in [:c :e] conj 13)
 (update-in [:c :e] conj 12)
 (assoc-in [:c :g] 14))
So if you could “thread-first” `example-map1` through that list of commands, it would become `example-map2`. I can accomplish this threading via
(eval `(-> example-map1 ~@(minimal-transactions example-map1 example-map2)))
=> {:a 1, :b 11, :c {:d 3, :e #{13 6 12 8}, :f 10, :g 14}}
But I think I should avoid `eval`. There’s probably a better way to do it. One thing that occurred to me was to make the output of minimal transaction a function, although then it’s not so easily readable by the user. The only way I could think to build that function iteratively (as I constructed the above transaction list iteratively, using `mapcat`, in the code I linked above) was to build the function with `reduce` and `compose`, turning each step into a lambda, and composing it at each stage to end up with the giant function which is the composition of all the transactions. That didn’t seem too great to me, either. Any suggestions? Possibilities: 1. Who cares about it being executable? It’s not explicitly stated in the problem, and someone can always figure out how to do that if they need to. 2. Who cares if the function is immediately readable? Someone can always go read the source code if they want to view the series of transactions. 3. Using `reduce` and `compose` that way to build a huge function is ill-advised because (I don’t know why). 4. People use `reduce` and `compose` together all the time to put a big function together out of little pieces; don’t be scared. 5. ? Thanks Clojurians, happy to provide more info (on top of this post which is already a bit long 🙂) if you want. Let me know if this is better placed in the “code review” channel. Didn’t seem like there was much activity there.