clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Saikyun 2020-09-18T07:57:09.016500Z

is there any way for me to setup a development environment using clj, on a computer that doesn't have direct access to the internet?

Saikyun 2020-09-18T07:58:06.017700Z

I want to be able to: 1. install clojure (it's a windows machine) 2. pre-download all dependencies and transfer them from my local machine to that computer

Saikyun 2020-09-18T07:58:25.018100Z

so that I'm able to run clj on that machine without it trying to download dependencies

Saikyun 2020-09-18T07:58:39.018500Z

setting up a local maven repo might be a solution, but I have no idea how to do that / how hard it is to do

viesti 2020-09-18T08:10:59.019200Z

hmm, how do add a writer for native Java arrays for Transit?

user> (require '[cognitect.transit :as transit])
(import [<http://java.io|java.io> ByteArrayInputStream ByteArrayOutputStream])
niljava.io.ByteArrayOutputStream
user&gt; (def out (ByteArrayOutputStream. 4096))
#'user/out
user&gt; (def writer (transit/writer out :json))
#'user/writer
user&gt; (transit/write writer [1 2])
nil
user&gt; (.toString out)
"[1,2]"
user&gt; (transit/write writer (into-array Long [1 2]))
Execution error (NullPointerException) at com.cognitect.transit.impl.AbstractEmitter/marshalTop (AbstractEmitter.java:203).
null

Helins 2020-09-18T08:22:09.021800Z

So, I have been reading a few things about Clojure and GraalVM. There is not much about using both for AWS lambdas, but it seems promising. Has anyone been experimenting with this? I am especially more interested in knowing the tradeoffs Vs using Cljs (performance-wise, not regarding the library ecosystem)

Saikyun 2020-09-18T08:35:42.021900Z

I don't know anything about lambda functions specifically (I'm guessing you mean AWS lambda?), but overall graalvm vs cljs the tradeoff is: • building graalvm takes a long time • startup is ~same (I think <0.01s) • graalvm faster runtime overall

👍 1
Saikyun 2020-09-18T08:39:37.022400Z

these are my experiences, don't have any hard numbers to back it up 🙂

Saikyun 2020-09-18T08:40:02.022600Z

I think @sogaiu has more to say on the subject

Helins 2020-09-18T08:41:06.022800Z

I heard that GraalVM became quite a bit more friendly towards Clojure (eg. plays nicer with common libs), how true is that in your experience?

2020-09-18T08:45:16.023100Z

i think a good person to ask that question to would be borkdude. on the subject of clojure working better with graalvm, i think there was some work on the clojure side toward this end. see here for details: https://github.com/lread/clj-graal-docs

Saikyun 2020-09-18T08:45:48.023500Z

personally, I've mostly used the "talk to c" capabilities of graalvm: https://github.com/Saikyun/clobits so don't know much about clojure libs

Saikyun 2020-09-18T08:46:01.023800Z

this page lists some of the libs that are compatible with graalvm 🙂 https://github.com/BrunoBonacci/graalvm-clojure

2020-09-18T08:47:33.024400Z

probably it's good to inquire at #graalvm for this type of thing

2020-09-18T08:48:32.024600Z

in the prerelease version of clj, i think there's a -P -- you might try #tools-deps for related inquiries

Helins 2020-09-18T08:49:37.024800Z

@sogaiu You're right, my bad

Saikyun 2020-09-18T08:51:22.025Z

thanks : )

👍 1
Josef Richter 2020-09-18T10:25:02.025600Z

Is anyone successfully running Clojure on Big Sur, please?

2020-09-18T11:36:27.026500Z

I haven’t had the chance to dabble much with Clojure lately, but I’ve upgraded to Big Sur and the Clojure REPL works as expected. What are the problems you’re facing?

Josef Richter 2020-09-18T11:48:51.026700Z

@hkjels I was unable to install clojure via homebrew. solved it by installing manually.

2020-09-18T11:56:09.026900Z

Ahh, OK. Nice that it panned out!

fabrao 2020-09-18T11:57:02.027600Z

hello all, Is there any way to limit memory usage of clojure program?

borkdude 2020-09-18T11:59:52.028100Z

@fabrao -Xmx and similar Java options are one way to limit memory

fabrao 2020-09-18T12:00:05.028500Z

like java -Xmx512m ?

fabrao 2020-09-18T12:00:49.029100Z

limit using 512Mb?

borkdude 2020-09-18T12:01:07.029500Z

Yes. That's only for the heap size though, there are also other resources that take memory

fabrao 2020-09-18T12:06:27.030300Z

so, my clojure program can get more than 512 Mb?

fabrao 2020-09-18T12:06:47.030600Z

what is the other resources?

alexmiller 2020-09-18T12:35:38.031Z

Unable how?

2020-09-18T13:37:20.035Z

RoamResearch is written in Clojure/Script?

p-himik 2020-09-18T13:41:03.038400Z

I have a DAG of Model records that I need to serialize. Each model has an :id and some attributes that may contain another model, a collection of models, a map of keyword -> model, or any other Clojure value. Right now I have this code:

(defrecord Ref [id])

(let [acc (atom (transient {}))]
  (postwalk (fn [item]
              (if (satisfies? Model item)
                (let [id (:id item)]
                  (swap! acc assoc! id item)
                  (Ref. id))
                item))
            roots)
  (persistent! @acc))
The atom is there because assoc! can return a new transient collection. The question is, can I rewrite this code so it stays simple but doesn't rely on atom anymore? I couldn't think of anything better than a number of nested loops or calls to reduce, and that's far from being simple.

p-himik 2020-09-18T13:43:29.038500Z

From https://roamresearch.com/#/app/help/page/Vu1MmjinS: > The Roam stack: > React > Clojure and Clojurescript > Datalog

2020-09-18T13:44:50.040800Z

Thanks @p-himik . I just registered today on Roam after reading a few times headlines about it. After getting excited I checked for the team and found a page about Conor...

2020-09-18T13:45:07.041500Z

No wonder why its rocking 😆

2020-09-18T13:45:30.042Z

Cool shit 💪:skin-tone-2:

alexmiller 2020-09-18T13:54:14.043200Z

you often see things like this with walking/updating state while you do it and I think it's perfectly fine if you're using walk (you might find volatiles to have slightly lower overhead though and sufficient for single-threaded use case)

alexmiller 2020-09-18T13:57:07.044Z

another option is to use zippers but I'm not sure it's any better for this

borkdude 2020-09-18T14:01:58.045700Z

this is a pattern I've also used in a couple of places (with volatile!) (e.g. discovering used params in a function body)

borkdude 2020-09-18T14:03:14.046400Z

actually, the entire clj-kondo analysis is basically a walk + swap! to a couple of atoms

p-himik 2020-09-18T14:03:45.047Z

Thanks! That's reassuring. :) Right, I completely forgot about volatiles.

ghadi 2020-09-18T14:08:27.047500Z

recursive for or mapcat are nice functional options, too @p-himik

ghadi 2020-09-18T14:09:10.047700Z

p-himik 2020-09-18T14:16:17.048900Z

Thanks!

2020-09-18T14:23:42.051Z

The above use of swap! for atoms that contain mutable things (transients in the example above) is safe for single-threaded use, but can lead to incorrect results if you try to do it from multiple threads, I believe? I am not saying "Don't do it in a single-threaded case", just trying to make sure for myself when it is unsafe.

alexmiller 2020-09-18T14:36:26.052500Z

putting mutable things in atoms is safe re visibility (atoms guarantee visibility)

alexmiller 2020-09-18T14:36:56.053100Z

atoms are an issue only if you're trying to coordinate changes in multiple mutable things (as they don't do coordination)

dpsutton 2020-09-18T14:37:23.053300Z

what do you mean by visibility here?

alexmiller 2020-09-18T14:40:10.055500Z

changes in non-volatile fields are not necessarily visible to other threads for arbitrary periods of time. Clojure largely protects you from issues around this by a) using immutable values (so changes are not a thing in the first place) and b) providing tools for safe change (atoms, refs, etc)

👍 1
alexmiller 2020-09-18T14:40:41.056100Z

transients are kind of a special case - they are "local" mutability

jsn 2020-09-18T14:41:04.056600Z

@andy.fingerhut swap! is safe, only e.g. deref + reset! is not, I guess?

alexmiller 2020-09-18T14:41:27.056900Z

that's still "safe" in terms of well defined semantics

alexmiller 2020-09-18T14:41:30.057100Z

but it is a race condition

jsn 2020-09-18T14:42:00.057900Z

Perhaps that race condition might be what @andy.fingerhut meant

2020-09-18T14:42:15.058200Z

Doing assoc! on a transient collection from multiple threads would be unsafe in many cases, true?

alexmiller 2020-09-18T14:42:24.058600Z

yes

alexmiller 2020-09-18T14:43:12.059500Z

you need some way to guarantee that a) the result of the transient op is used for the next op and b) other threads see those changes

alexmiller 2020-09-18T14:43:26.059900Z

basically, serializability is the answer to both, which atoms give you

2020-09-18T14:44:01.060700Z

Sure, atoms give you visibility of changes, but assoc! by multiple threads on the same transient is going to give you visibility of garbage. (edit: not garbage in the gc sense, but in the sense of "a mutable object that has been banged on by multiple threads in parallel, and they have likely messed up its internal state)

alexmiller 2020-09-18T14:49:47.061200Z

it's not the same transient if you're using assoc! in a swap!

alexmiller 2020-09-18T14:50:06.061600Z

swap! is replacing the value with the result of the assoc!

alexmiller 2020-09-18T14:51:33.062400Z

I guess I should it may or may not be the same transient, but the important thing is that you follow the same usage pattern as assoc

ghadi 2020-09-18T14:53:12.062800Z

I find atom+transient harder to reason about

ghadi 2020-09-18T14:53:29.063200Z

atom + persistent -> no brainer

ghadi 2020-09-18T14:55:24.065100Z

even though the atom usage above is scoped to a single function and doesn't escape threads... Clojure makes no promise that your swap! will be called only once

isak 2020-09-18T14:56:53.066400Z

Is the transient guaranteed to be different when you call assoc!? I thought it was just that it may return a different value

alexmiller 2020-09-18T14:57:22.067Z

@isak it will not always be a different value - sometimes the current one is modified and returned

alexmiller 2020-09-18T14:57:39.067700Z

@ghadi that's a good point and could indeed cause problems

ghadi 2020-09-18T14:57:42.068Z

atom+transient is inappropriate, IMHO

alexmiller 2020-09-18T14:57:50.068300Z

I recant! :)

isak 2020-09-18T14:57:51.068400Z

Ok thought so

isak 2020-09-18T14:58:07.068700Z

How about volatile + transient though?

isak 2020-09-18T14:58:24.069400Z

I think it is ok, because then the expectactions are different

isak 2020-09-18T14:58:35.070100Z

volatile is only about usage from 1 thread, right?

alexmiller 2020-09-18T14:59:44.071Z

not necessarily

isak 2020-09-18T15:01:36.072300Z

I got that impression from this part of the release notes: > volatiles - there are a new set of functions (volatile!, vswap!, vreset!, volatile?) to create and use volatile "boxes" to hold state in stateful transducers. Volatiles are faster than atoms but give up atomicity guarantees so should only be used with thread isolation.

2020-09-18T15:02:21.073600Z

@ghadi I was thinking that atom+transient (or any other mutable object in general) + single-thread only might be safe, which is the context where it was originally brought up in this discussion. It is atom+transient (or any other mutable object in general) + multiple threads concurrently that I was double-checking my understanding about. But maybe I'm wrong that single-thread is safe there.

ghadi 2020-09-18T15:03:17.074600Z

hypothetically if we used weakCompareAndSet instead of compareAndSet under the hood, it could possibly retry even in one thread

1
ghadi 2020-09-18T15:03:43.075200Z

besides, your function should have a budget of one mutative thing :)

isak 2020-09-18T15:06:03.076100Z

I wonder if vswap! is guaranteed to only run once, unlike swap!

isak 2020-09-18T15:11:08.076300Z

Looks like it, at least with the current implementation: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Volatile.java#L25-L27

Josef Richter 2020-09-18T15:14:20.077Z

sorry, missed your message. basically brew tells you this package is not compatible with big slur, so good bye

alexmiller 2020-09-18T15:22:28.077200Z

thread isolation here means 1 thread at a time not only one thread so you should be careful about the difference

2020-09-18T15:24:18.077400Z

Huh, hadn't seen weakCompareAndSet before. Sounds like something only an expert implementer of concurrency mechanisms might ever use?

isak 2020-09-18T15:26:07.077600Z

Got it, thanks

2020-09-18T16:05:21.077800Z

That is some rule-of-thumb for mutable data? Seems like a reasonable one.

benoit 2020-09-18T16:25:43.078300Z

Good example of how introducing some local state can sometimes make functions much easier to read 🙂

benoit 2020-09-18T16:27:46.078500Z

I'm being a bit unfair, the big difference in readability might be mostly due to the use of postwalk compared to the custom traversal in the functional style.

p-himik 2020-09-18T16:32:57.078700Z

Yeah, it's a useful function in this case. But on the other hand, with a custom traversal code I could do that traversal in a more efficient way.

benoit 2020-09-18T16:36:40.078900Z

Yes, it is always the same trade-off in programming :)

walterl 2020-09-18T16:39:36.079900Z

Is this a superfluous indent in select-keys, or am I missing something? https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L1545

alexmiller 2020-09-18T16:45:44.080500Z

Clojure source is the result of a large number of patches so it's not particularly regular

alexmiller 2020-09-18T16:47:35.081100Z

looks like that particular one was a mass re-format from Rich, must have been something off with whatever tool was used

ghadi 2020-09-18T16:48:02.081700Z

AtomicReference itself uses a weak CAS internally

ghadi 2020-09-18T16:48:07.081900Z

yes, expert stuff

alexmiller 2020-09-18T16:48:26.082300Z

that was 11 yrs ago, so who knows

2020-09-18T19:02:05.084100Z

I was also wondering why there are large chunks of commented-out code still around? Ex: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java#L819 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Numbers.java#L2014 I assumed it was just a “move fast, break things” kind of result, but perhaps there’s another reason? In any event, not being critical, just curious.

2020-09-18T19:03:24.084700Z

@jeffrey.wayne.evans that’s a rich comment

dpsutton 2020-09-18T19:03:47.085400Z

Not in a Java file

2020-09-18T19:03:52.085600Z

e.g. (comment …bunch-of-helpful-forms) at the bottom of a file

dpsutton 2020-09-18T19:04:18.086500Z

The linked bit is just commented out Java code

2020-09-18T19:04:18.086600Z

It is the closest one can get in a Java file to a Rich comment containing test code for the code in the same file, I think.

2020-09-18T19:04:46.086900Z

@dpsutton does it make a diff?

2020-09-18T19:05:11.087900Z

“here is some code you might find helpful when messing w/ this class”

dpsutton 2020-09-18T19:05:16.088200Z

Ha a little bit but I thought it was outdated but didn’t realize it was a separate main for testing

2020-09-18T19:05:23.088400Z

yep

2020-09-18T19:06:17.089Z

which I assume was the result of some exploration

2020-09-18T19:07:39.090500Z

It looks like the last one is in inner class ChunkedSeq, so potentially a copy-and-paste from similar inner classes, then realized that he never wanted to do assoc or conj to increase the size of a ChunkedSeq object, so commented it out. Hard to tell sometimes the history/reasons, unless it is somehow clear from the git commit history, but even then not always.

2020-09-18T19:09:11.090700Z

yep

dpsutton 2020-09-18T19:09:52.091300Z

i've seen these blocks before and always just assumed it was abandoned features or commented out and didn't think they were kinda debug/introspection bits

2020-09-18T19:10:49.092Z

Yeah, that was sort of my assumption as well, but I figured there would be a comment just above that with a short explanation as to why. You can glean some of that by checking the history, of course.

2020-09-18T19:14:50.094700Z

In general, there is very little answer to 'why' in Clojure's implementation code that I have found. There are some answers to those questions in some of Rich's talks, which you can find transcripts for most of them and links to videos here: https://github.com/matthiasn/talk-transcripts. None of those answers are 'why' at the detail line-by-line code level, but at a higher level of explanation. For line-by-line kinds of questions, the best source I know of is asking here or in #clojure-dev to see if anyone knows. There might be some answers in the book Clojure: The Essential Reference, but I do not know how closely any reasons given there correspond to the reasons of the Clojure developers.

ghadi 2020-09-18T19:15:25.095300Z

lots of the commented out static main methods are benchmarks

alexmiller 2020-09-18T19:47:52.095900Z

sometimes Rich keeps explorations around in comments. it's Rich's world, we just live in it.

🙂 1
firstclassfunc 2020-09-18T21:45:14.097400Z

Hello all. is there anyway to get to the underlying map type for a lazy seq? (type (doall (lazy-seq {:1 2 :3 4})))

ghadi 2020-09-18T21:49:35.097700Z

@gary001 what are you actually trying to achieve?

firstclassfunc 2020-09-18T21:50:12.098400Z

I have a return value that might be a vector or map and I need to dispatch off that type

ghadi 2020-09-18T21:51:18.099100Z

you can check if something is a vector? or map?

ghadi 2020-09-18T21:51:29.099600Z

but that is separate from being included in a lazy-seq

firstclassfunc 2020-09-18T21:51:31.099700Z

yes but its a lazy-seq

firstclassfunc 2020-09-18T21:52:00.100100Z

I need to realize the lazy-seq to find its type.

ghadi 2020-09-18T21:52:15.100300Z

sorry I'm not quite following

ghadi 2020-09-18T21:52:28.100600Z

an element knows not what it is contained in

firstclassfunc 2020-09-18T21:53:03.100900Z

(type (doall (lazy-seq {:1 2 :3 4})))
=&gt; clojure.lang.LazySeq
(type (doall (lazy-seq [1 2 3 4])))
=&gt; clojure.lang.LazySeq

seancorfield 2020-09-18T21:59:24.102800Z

Once you've turned the map into a lazy sequence, it's a sequence (of MapEntry items) -- it is no longer a map. Same with turning a vector into a lazy sequence -- it is no longer a vector. (and this feels more suitable for #beginners -- not sure where you are on your journey @gary001?)

dpsutton 2020-09-18T21:59:52.103300Z

i think this is a bit like the underlying type of the result of (+ (float 1) (float 1)). its a double regardless of how you got there. lazy-seq produces a new value

firstclassfunc 2020-09-18T22:03:17.104100Z

@seancorfield Thanks thats what I thought, I am consuming from an external library so have to find another way.. thanks

seancorfield 2020-09-18T22:04:21.105200Z

@gary001 So you're getting the lazy sequence from the library? Which library is it? I'm curious as to how/where the map vs vector dilemma comes into such code...

firstclassfunc 2020-09-18T22:12:30.107800Z

@seancorfield http response from my server can return data in different shapes and I am trying to dispatch my output based off of the type so I can format the results.

seancorfield 2020-09-18T22:13:51.108Z

@gary001 Still not following how the lazy seq aspect plays into this... can you elaborate? (and I figured we'd take it to a thread rather than clog up the main channel at this point)

seancorfield 2020-09-18T22:14:56.108200Z

Which library is returning a lazy seq where you need to find our what the lazy seq was originally built from?

dpsutton 2020-09-18T22:16:10.108400Z

seems strange to get a lazy-seq back as a response from a webserver. that doesn't really make sense to me