Fork me on GitHub

Use a Clojure set, e.g. #{:foo :bar :baz}


nb this works with any plain values, except false or nil (I know the question was only about keywords)


like this example from (s/def ::suit #{:club :diamond :heart :spade})


So you want a map that can have an arbitrary subset of the keys ::north ::south ::east ::west, and the type of the values associated with them will be the same for each?


You never want to allow a subset of the 4 possible keys, i.e. you want spec to check that all 4 are always present


I guess that is fine, if some of them are walls 🙂


If you had 100 keys all with the same type, I would definitely agree. 4 seems on the edge of "eh, it gets the job done" to me, personally.


That seems pretty important when they have a large variety of specs for each of their corresponding values, doesn't it?


My understanding is that a spec key name is intended to be used throughout a possibly multi-process multi-machine system, and you want the name of that key to have the same kind of contents wherever it appears, in any map where it appears.


e.g. :com.mycompany/customer-id will always have the same kind of associated values, no matter where it is, no matter what it is combined with in whatever maps it ends up in.


I do not know if I understand your use case well enough. Are you saying you might have 10 different kinds of cells, and for each of those 10 kinds, you want to constrain the set of allowed values differently for their :north key?


Sure. I do not have enough spec time under my belt to say whether it was designed to be bent in that direction, or whether you are stretching it unnaturally against its design.


@deleted-user If you go in a direction you get to a (type of) cell, yes?


(assuming that direction is available to you)


When you are in a cell, each of the directions has a wall/door/passage/etc that determines whether you can go that way (and how you do it), yes?


If you had only 2 types of grids, with different sets of values of things you wanted to allow in the north direction for those 2 types, it seems worth asking whether having a :grid1/north and :grid2/north spec might be useful?


And then there's the actual motion from cell to cell, so each direction takes you to a new type of cell...


What are the different types of grids?


Hum... What are you using Spec for though ?


All these intermediate steps don't necessarily need a spec. Or, not necessarilly a very granular spec


Yes, but why?


As documentation?


I'm saying that, because if your goal was validation, or generative testing, the choices for how and what to Spec might be different than other reasons to have the Spec


So, I don't necessarily want to suggest that, because it gets hairy. And I'd wonder if it is really necessary, but you can obviously write spec generating code.

Brandon Olivier03:06:12

I'm trying to use Luminus to make a POST request, but whenever I try to access the body I get a #object[ back. I've been trying to figure out what to do with that for like an hour and I still have no idea.


Which could help you create all the combinations of s/keys you need


Hum... are you saying the same key will have different type of value in different context?


I see, you could look into s/or and s/multi-spec


They allow you to specify that a particular thing is one of many things, with multi-spec letting you define what based on a function.


Alright, I didn't fully understood exactly what you're trying to spec, but I'm glad my pointers helped


Maybe if you posted an example of the data-structure in two different state?


@brandon149 hum... this is a wild guess, I've never used Luminus, but try calling it with an @ in front

Brandon Olivier04:06:21

@didibus I'm still a total n00bie, what's the @ do?

Brandon Olivier04:06:33

I remember using it with atoms, is that going to be the same kind of thing?


Well, normally it does a thing called deref. Which normally just mean get the value out of a thing.


It might not be that what you have can be derefed, so it might not work


But I thought there is a possibility that they would have designed it that way

Brandon Olivier04:06:51

That gives me an error java.lang.ClassCastException: class cannot be cast to class java.util.concurrent.Future

Brandon Olivier04:06:07

It's some kind of stream, but I'm not sure how to read it


Ah ok, so ya no, it doesn't work like that then 😛


It was a wild guess

Brandon Olivier04:06:20

most of the java code has something like while ( > -1)

Brandon Olivier04:06:28

but that doesn't really translate to clojure as far as I know

jason poage04:06:47

i want to start my program with a function other than -main, how do i do that without starting the repl first?


lein run -m your.namespace/the-function -- see lein help run for more information

jason poage04:06:21

i am using lein

Brandon Olivier04:06:28

That's an IllegalArgumentException

Brandon Olivier04:06:31

No matching field found: read for class immutant.web.internal.ring.LazyMap

Brandon Olivier04:06:49

The real source of my trouble is that I can't figure out how to read an UndertowInputStream


Well, depending what is on that stream, it could be complicated

Brandon Olivier04:06:13

I tried reading the docs for Undertow, but they weren't particularly helpful


I feel like Luminous should have already parsed the response for you

Brandon Olivier04:06:25

which isn't surprising, given my lack of Java experience

Brandon Olivier04:06:31

@didibus you and me both, man


haha, I'm just wondering if you are calling something lower level that returns the raw stream, and maybe there is another way to make a POST request that returns it parsed?

Brandon Olivier04:06:18

Given that I'm attempting to add a new ns of routes, it's probably that I forgot to add something required

Brandon Olivier04:06:33

I know the original one included some middleware, but I didn't include it


Try calling it with slurp?


That could read some type of streams

Brandon Olivier04:06:15

I tried that, it gives me an error something about how it's not a file


Clojure has arbitrary looping and the ability to call the read method


Try calling on it first maybe?


and then slurp ?


lein run -m your.namespace/the-function -- see lein help run for more information


Except you have an UndertowInputStream instead

Brandon Olivier04:06:07

So, after using , I end up having errors about not being able to JSON encoding a ByteArrayInputStream

Brandon Olivier04:06:12

which, again, seems pretty similar


Something is weird, when you called (.read body) you got: immutant.web.internal.ring.LazyMap ?


But when you call slurp on it you get an exception mentioning: UndertowInputStream ?


You should be able to call .read on the UndertowInputStream. The issue is that it seems to be a byte stream. So you will just get a bunch of bytes, and then you need to know the encoding, so you can decode the bytes into whatever they represent


You 100% did not get lazymap back from calling read on a stream


Variations on a lazy maps are used in a number different ring adapters for the request or the headers map


aleph does this, I haven't used undertow in a long time, so it looks like they do this too


Lazy map being a map that doesn't have it's values filled in yet because the adapter is built on some kind of asyncy server that doesn't get the entire request at once


I think you are probably missing a middleware which should decode the undertow stream or something. I really doubt Luminous doesn't handle the byte stream for you

Brandon Olivier04:06:13

I'm getting weirdly inconsistent results in CIDER and I'm not really sure why

Brandon Olivier04:06:34

now, I call .read on (:body req) and it comes back with 123


Inputsteam is and interface, and UndertowInputstream implements it


Correct, calling read on an inputstream returns and int


(because it can't return byte for reasons)

Brandon Olivier04:06:02

Well, that's good, I guess


Slurp will work on an inputstream

Brandon Olivier04:06:24

at least the results making sense now


My guess is you were confused earlier and called in on the request map or the header map


Try slurp again

Brandon Olivier04:06:03

slurp is working now, but I don't understand why


Because you were confused earlier

Brandon Olivier04:06:37

Something weird is going on, because I get different results from the POST call, but they're not consistent


And were not doing what you thought you were doing

Brandon Olivier04:06:52

The code I have written right now is identical to something I wrote earlier

Brandon Olivier04:06:55

but the results aren't the same


Could the post be async?

Brandon Olivier04:06:54

it's possible, I'm not particularly familiar with Luminus, and especially not Undertow

Brandon Olivier04:06:58

I think what was wrong with my debugging

Brandon Olivier04:06:17

was that, for reasons I don't understand, I need to evaluate the function I'm looking at

Brandon Olivier04:06:24

and then (restart) the app

Brandon Olivier04:06:35

I did not consistently do those things in that order


Hum.. could be. But, it might be you just accidentally were calling slurp on the wrong thing.

Brandon Olivier04:06:05

It's possible. I'll ascribe the whole thing to my newbie status and hope to learn from it


I'm playing around with the AWS API, so far I've been using amazonica, but I can't make head nor tails of how to set up a client, so I thought I'd try out Cognitect's AWS lib ( However, I'm completely failing to convince lein to download it, and there are only instructions for using deps.edn 😕 Any pointers?


Where it shows this in the README, to use for deps.edn:


{:deps {       {:mvn/version "0.8.305"} {:mvn/version ""}        {:mvn/version "722.2.468.0"}}}


Yes, indeed... how do I translate that to something that lein will accept?


In a Leiningen project.clj file, that would become the following lines in that file:


:dependencies [
                 [ "0.8.305"]
                 [ ""]
                 [ "722.2.468.0"]


HI! have you ever done 2 projects in the same git repository with lein? :thinking_face: e.g I'm doing a client/server project, and I would like to have both on same repository instead of creating an organisation


you can probably do it in one file, i have in the past just used the cljs-build options to differentiate the 2. If you need more separation, eg diff dependencies etc (idk why you would) you can use profiles and the lein with-profile +whatever ... command


you would just run 2 different commands to start up each ones repl in develop, and make a command that builds both for deploy


Thx I will try out


I couldn't find out any documentation flag for it


so I ended up to doing 2 directory each one with a seperate project.clj


It is maybe not super beautiful but I didn't want to spend all the time researching it. The leinigein doc didn't have doc for that. And I think in JVM is possible to do it but I'm not the JVM expert 😁


if anyone has a link feel free to share.. 🚀


can you link your project (if its opensource, etc)


make a new project doing lein new chestnut app-name and look at it, it is a template that serves up a routes and compiles a frontend

clj 4

I'm looking currently if I can do it with only 1 project.clj file only or if i need to create directories with 2 project.clj


Hi. I'm trying to create a random matrix. I'm using the matrix.core library... (clojure.core.matrix/add (rand 5) (clojure.core.matrix/new-matrix 2 2)) But this gives me a matrix where every element is the same random number e.g. [[3.928259253657548 3.928259253657548] [3.928259253657548 3.928259253657548]]


I tried using repeatedly but the library doesn't handle it the same way normal clojure vectors handle it, so I haven't been able to make use of it


(`(clojure.core.matrix/emap + (repeatedly #(rand 2)) [ 1 1 1 ] )` this actually causes a crash, whereas normal map is fine)


(clojure.core.matrix/emap #(rand 2) (clojure.core.matrix/new-matrix 2 2)) may be what you are trying to do

Charles Fourdrignier12:06:48

Not sure that's a "good solution", but last time I needed to generate a matrix, I relied on clojure.spec + generators...

(def MATRIX-SIZE 100)
(s/def ::image-pixel #{0 1})
(s/def ::image-row (s/coll-of ::image-pixel :count MATRIX-SIZE))
(s/def ::image (s/coll-of ::image-row :count MATRIX-SIZE))

(gen/generate (s/gen ::image))


I've slightly modified @U0C7D2H1U code and it does indeed produce the expected matrix (clojure.core.matrix/emap #(+ % (rand 2)) (clojure.core.matrix/new-matrix 2 2))


I will stick to this for the time being since it accomplishes the task and it's moderately readable. Thanks guys


Hi! I am looking to get some help on getting my installation of clojure working correctly. I am running Ubuntu 18.04 and have installed the prerequisite bash, curl, rlwrap and java, with the latter being

$ java -version
openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu218.04.1)
OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu218.04.1, mixed mode, sharing)
Some basic functionality works fine, like (def x 7) or (* 8 8), but while working through the official guide ( I often walk into java exceptions for code that does work on, like
Clojure 1.10.0
user=> (def anex (try (/ 1 0) (catch Exception ex ex)))
user=> (clojure.stacktrace/root-cause anex)
Execution error (ClassNotFoundException) at (
Does anybody know what exception is actually occurring and how to fix this? I could only find an SO thread mentioning java 8+, but I think I have that installed (I am unsure, I'm not a java programmer). Thanks in advance.


you need to require clojure.stacktrace


How do I require that library?


it is a namespace, and it is part of clojure, so you just need to (require 'clojure.stacktrace)


some of the namespaces that ship with clojure are required elsewhere so end up being sort of implicitly loaded, but the set of namespaces that get loaded like that have changed overtime so it is a best practice to require them if you need them


Right, that clarifies a lot.


The example I gave also works when I require the library, so thanks for helping!


I guess just autoloads a bunch of namespaces then. 😛


it likely loads clojure.stacktrace for pretty printing exceptions or something


if code is loaded, you can use it without requiring it


That seems reasonable.

Noah Bogart17:06:32

in javascript, i can say cost game = new Game(); let card = new Card(game);;, which creates a recursive relationship between the two objects. It's useful so inside the Card class, you can say if ( { do_stuff(); }. Is that possible in Clojure? If not, what are the best idioms to handle that kind of relationship?


usually instead of recursively nested values, I'd use a flattened non-recursive structure that describes both relationships


there's no construct or shortcut to create mutual nesting, so you need to use some trick with laziness or mutation


and it's not worth the headache or complexity IMHO


(by flattened non-recursive structure describing relationships I of course mean adjacency list)


to reiterate what noisesmith said: you can do this using mutable values but it is very un-idiomatic


so = foo is instead {:foo {:bar :bar} :bar {:parent_foo :foo}}

Noah Bogart17:06:48

hm! that's an interesting idea!


the drawback is that instead of indexing the adj-list directly, you need a function that knows how to walk it


the advantage is you can describe complex recursive relations in immutable data, and the editing operations are much less error prone than the mutable version


also you don't run into stack overflow errors printing it naiively :D

Noah Bogart17:06:15

heh That is a benefit!


Another option: let Game and Card instances have unique IDs so they can store references to external instances


in practice it's more complex than my example above - more like {:edges {:foo #{:bar}} :nodes {:bar {:parent-foo :foo}}

Noah Bogart18:06:04

that's another interesting idea, @john. i'll have to think about that!


you can look at for inspiration

💯 4

although it is pretty heavy and might not be too much


Taking that route, you'd have to manage those references in some state somewhere


But that can be done functionally


@john yeah - the adj-list stores the references and the data in one immutable structure for example, but can use unique ids instead of symbolic names


then it's effectively the same thing

Noah Bogart18:06:46

as background: i've been playing with porting a pipeline/queue system from a game engine written in javascript to clojure, for potential use in a card game i maintain. the idea is that the pipeline queues "steps" (which themselves can contain pipelines), and they share common interfaces such as being able to say pipeline.continue() which will then grab the first step in the queue and call that step.continue() which then looks to see if it has it's own pipeline or whatever

Noah Bogart18:06:19

i feel like i'm going about this the wrong way, but i still feel very new to functional/immutable programming, so trying to make stuff like this work has been kind of breaking my brain lol


@nbtheduke another thing to look at for this kind of game state manipulation are Entity Component Systems, which translate very nicely into immutable data in hashmaps

Noah Bogart18:06:42

That's pretty close to what the javascript engine does, and is definitely not what the clojure engine does, lol


and for simple mechanics of queues, use clojure.lang.PersistentQueue/EMPTY which sadly doesn't have a nice looking data literal


of course nothing prevents you from doing (def | clojure.lang.PersistentQueue/EMPTY)

Noah Bogart18:06:28

lol |, that's pretty clever


@noisesmith do you think he could model his game on core.async machinery? Sounds like he wants to put channels in channels, or some similar abstraction


channels are an io mechanic, and queues are a data structure


they often go together but not always - I'd hold off on async until the domain requires it honestly


(cmd)user=> (def | clojure.lang.PersistentQueue/EMPTY)
(cmd)user=> (-> | (conj :a :b :c) (pop) (conj :d) seq)
(:b :c :d)


Aye, harder for me to think functionally in core.async too


right - it's an IO framework not a functional abstraction

Noah Bogart18:06:53

I looked into the PersistentQueue, but my issue with it is there's no easy method for prepending things


you need a double ended queue?


I mean, usually queue means fifo, which PersistentQueue does

Noah Bogart18:06:44

potentially! i'm not quite sure

Noah Bogart18:06:46

Like, I perform an action that then requires reactions, so i'd like to put the reactions before the rest of the queued actions


there's DeQueues built into the vm, but none immutable, and none in clojure itself


of course via into and concat you can force it


(ins)user=> (def q (-> | (conj :a :b :c) (pop) (conj :d)))
(ins)user=> (seq q)
(:b :c :d)
(ins)user=> (into | cat [[:a] q])
#object[clojure.lang.PersistentQueue 0x736d6a5c "clojure.lang.PersistentQueue@298384c8"]
(ins)user=> (seq *1)
(:a :b :c :d)


the prepends are more expensive, but they are possible, and if they are rare it might be worth it(?)

Noah Bogart18:06:54

they would not be rare


you could look into finger-trees if you are stuck on immutability or use a built in Deque


there's no issue with data safety in concurrency in js


js has no "at the same time"


it's via returns / callbacks


so it's cooperative, there's no interruption


there are concurrency frameworks, but they are driven by providing and returning from callbacks, they don't interrupt code


in jvm clojure where we do have arbitrary interruptive context switches, the combination of immutable data structures and safe containers like atom / ref / agent are sufficient if used correctly


but there's a whole class of errors (all the data integrity stuff) that just doesn't happen in a single thread like js


so that data.priority-map linked above - another thread can't corrupt it because it's immutable, and the workings of atom would ensure that the replacement of one reference to an immutable map by another is done safely


@deleted-user precisely, it's classic cooperative concurrency


right, and using an atom correctly prevents this


and in js, it's simpler because it suffices to not return until the data is "coherent" by your definition


Use clojure.core/compare-and-set!


@deleted-user the tl;dr version is your function passed to swap! is written so that it does all the operations for an update in one go, and either returns a valid state or errors


even swap! suffices, but swap-vals! is useful sometimes as well


Use compare-and-set!


Swap! Is not what you want


You don't need to shoe horn it in to swap!

Noah Bogart18:06:15

What's the difference between swap! and swap-vals!?

Noah Bogart18:06:00

is it just that swap-vals! returns both old and new?


I've also got a mutable queue that lets you use cas to pop values


(I have a PR to that queue )


I didn't mean to offend. I enjoyed studying the code and linked paper. I thought i identified a small bug and sent a patch to fix it. Didn't mean to annoy


oh no, I just haven't looked at it in a while


ah ok. yeah i mentioned it now just because it was on your mind 🙂


Hi. I’m doing something boneheaded with Everything works fine when going against a local database, but when I try to connect to a remote I get exceptions saying my connection string is probably wrong. I’m creating a datasource by doing something like this:

(->> {         :user "myuser"
         :password "very-secure-pw"
         :dbname "energy"
         :dbtype "mysql"
         :host ""
         (reset! datasource))


I can connect to mysql using the command line just fine at that host: mysql -u 'myuser'@'machinename' -p -h energy works fine


I’ve tried changing the user in the config map, using “myuser”, “myuser@machinename” and “‘myuser’@‘machinename’”


next.jdbc/get-datasource is returning nil with those parameters.


@credulous I'm not sure how get-datasource can return nil at all here. It should either throw an exception or return a javax.sql.DataSource object. It would also really help us to help you if you could share the exact exception you are getting for "my connection string is probably wrong"... Lots of exceptions are possible if you don't have things set up correctly 🙂


Thanks Sean. This is the exception I’m seeing: (the first line is just from a (println @datasource) in my code)


19-06-25 20:44:02 MacBook-Pro.hitronhub.home INFO [] - Executing sql against datasource next.jdbc.connection$url_PLUS_etc$reify__1548@3ea730b3
19-06-25 20:44:03 MacBook-Pro.hitronhub.home ERROR [] - SQLException The connection string may not be right. Please visit portal for references.
        com.mysql.cj.jdbc.exceptions.SQLError.createSQLException (
        com.mysql.cj.jdbc.exceptions.SQLError.createSQLException (
        com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException (
        com.mysql.cj.jdbc.ConnectionImpl.createNewIO (
        com.mysql.cj.jdbc.ConnectionImpl.<init> (
        com.mysql.cj.jdbc.ConnectionImpl.getInstance (
        com.mysql.cj.jdbc.NonRegisteringDriver.connect (
        java.sql.DriverManager.getConnection (
        java.sql.DriverManager.getConnection (
        next.jdbc.connection/get-driver-connection (connection.clj:78)
        next.jdbc.connection/get-driver-connection (connection.clj:74)
        next.jdbc.connection/url+etc/reify--1548 (connection.clj:145)
        next.jdbc.connection/make-connection (connection.clj:162)
        next.jdbc.connection/make-connection (connection.clj:152)
        next.jdbc.connection/eval1564/fn--1565 (connection.clj:181)
        next.jdbc.protocols/eval1358/fn--1359/G--1349--1366 (protocols.clj:24)
        next.jdbc.result-set/eval2004/fn--2012 (result_set.clj:449)
        next.jdbc.protocols/eval1390/fn--1421/G--1381--1430 (protocols.clj:33)
        next.jdbc/execute! (jdbc.clj:165)
        next.jdbc/execute! (jdbc.clj:155)! (mysql.clj:96)! (mysql.clj:91) (mysql.clj:102) (mysql.clj:101)$eval11520.invokeStatic (:105)$eval11520.invoke (:105)
        clojure.lang.Compiler.eval (
        clojure.lang.Compiler.load (
        user$eval11516.invokeStatic (:1)
        user$eval11516.invoke (:1)
        clojure.lang.Compiler.eval (
        clojure.lang.Compiler.eval (
        clojure.core/eval (core.clj:3214)
        clojure.core/eval (core.clj:3210)
        clojure.main/repl/read-eval-print--9086/fn--9089 (main.clj:437)
        clojure.main/repl/read-eval-print--9086 (main.clj:437)


The comment about datasource being nil was a red herring, sorry


And the remote MySQL instance is running... where?


Looks like Azure, based on that error.


Ah, OK, this is due to Azure's slightly weird username rules. Which version of next.jdbc are you using? (1.0.0 "gold" should print something more informative for the reified datasource but it will depend on how, exactly, you are printing it -- I would have expected the datasource to print the actual URL being used.


For example, with next.jdbc 1.0.0 (as opposed to the RC):

(! 573)-> clj -Sdeps '{:deps {seancorfield/next.jdbc {:mvn/version "RELEASE"}}}'
Clojure 1.10.1
user=> (require '[next.jdbc :as jdbc])
user=> (def ds (jdbc/get-datasource {:dbname "mydb" :dbtype "mysql" :user "admin" :password "secret" :host ""}))
user=> (println "this is the datasource" ds)
this is the datasource #object[next.jdbc.connection$url_PLUS_etc$reify__397 0x2a415aa9 jdbc:]
So I would expect to see the URL in the output from your println call.


Based on the Azure docs, I would expect :user to need to be myuser@machinename (which I know you said you tried but perhaps you could try it again?)


Thanks, Sean, I really appreciate the help. The issue was indeed the username. Your deduction skills are amazing.


And sorry for not replying right away, my tranquil home suddenly turned into a toddler-bedtime warzone


you might get a better answer on #sql, but my first instinct is that a db spec and a datasource are two ways to get a connection, and you are asking for the datasource out of a db spec


so I'd expect get-connection which would work on a map like yours, or a datasource


or some ssl auto-negotiation that the cmdline does that the jdbc driver does not


maybe it is about ssl etc.


Thanks, maybe I’ll mess with some ssl config.


I'd start with the MySQL JDBC driver docs


you may just have to put mysqls:/ or something simple


assuming this is a DB public on the internet

seancorfield19:06:07 and next.jdbc make no assumptions about SSL or not -- they leave that entirely up to the JDBC driver.


@credulous I'm happy to dig into more detail about this in #sql but if you can tell us exactly what error you're getting when connecting to remote, that's going to be a good starting point.


In next.jdbc, you can (and should) create a datasource from a hash map (unless you're building your own pooled datasource via Hikari/c3p0 etc), so the code is correct in that regard.

👍 4

(then you -- or next.jdbc -- can create connections from the datasource, via get-connection)


ahh- that was my confusion, I thought of a datasource vs. connection config as alternate inputs to get-connection, I guess this taxonomy makes more sense


It was kind of muddy in since everything was a hash map. next.jdbc works on the Java types (`java.sql.Connection`, javax.sql.DataSource)

Joachim Smith19:06:39

can anyone explain why

(into #{})
is faster than
? If both are the same, why ever use


hey all, wondering what the idiomatic way to traverse a tree and build up a seq of the results of meta calls on each node. kind of like reduce combined with postwalk. I’m sure there’s a simple solution, but I can’t quite wrap my head around an idiomatic way to do that


@mayanaze I'd have to see some specific code to comment on that. I just ran a few quick tests and didn't much difference between (set coll) and (into #{} coll)...


Based on the source, both will use reduce with conj! over a transient collection...

Joachim Smith20:06:47

@seancorfield thanks for the reply. the test was i was running is pretty simple. run both a couple of times and into is consistently faster

Joachim Smith20:06:26

the def is so the repl doesn't need to print 10000000 times


Did you use that actual code? nums will be realized the first time it is used, but then it will be a fully-realized sequence in the second call.


It might not be relevant, but what OS, JVM version, and Clojure version are you running?


and what kinds of run time results are you seeing?


When I run both of those defs several times with macOS 10.13.6, Oracle JDK 1.8.0_192, Clojure 1.10.1, the first one was about 21 seconds, but after that both of them varied between 7 to 10 seconds on different runs, where the longer times are likely due to more GC time when the JVM needed to free up some memory. The first time being much longer is likely due to the JVM byte code not being JIT'ed yet.


oh, yes, and the reason that Sean mentioned -- range and take do not actually create the sequence until you force them to, which will happen the first time you run one of the last two def's.

Joachim Smith20:06:01

i understand, which is why i run both tests multiple times and ignore the first one


And you see one of those two being consistently faster than the other on your system, across multiple runs? More than the variation in times of the same expression across multiple runs?

Joachim Smith20:06:13

here's a more robust test


things have changed


So regarding your original question, I would think that some people might prefer (set ...) over (into {} ...) for code readability reasons, although the difference is minor. I would say that while there are differences in the run times there, and there might even still be with Clojure 1.10, they are within about 10% of each other, so nothing I would lose sleep over. If there is a straightforward root cause that one of them really is consistently faster than the other, a performance-enhancing patch for the other one might be interesting.

Joachim Smith20:06:00

@hiredman have the functions changed?


the internals, yes


even the internal implementation of range is very different


(not sure exactly, that may be 1.6 but I think it might have been 1.7)


I think the big shake up of fast path reduce implementations, fast iterators, and faster range implementation are all post 1.6


1.7 is when we did that


but this is a non-sensical benchmark in the first place


no real world code will be dominated by these costs


into processes the collection via reduce, which is fast pathed now


I think set may still walk the seq, not sure


so what if there is a minor performance difference, either way

Joachim Smith20:06:56

another example:

Joachim Smith20:06:57

(time (dotimes [_ 100] (vec (range 100000))))

Joachim Smith20:06:05

(time (dotimes [_ 100] (into [] (range 100000))))


are you still on 1.6?


Unless you are looking for history of performance changes, the latest Clojure version is probably of most interest here.


as @hiredman mentioned, range + reduce/into has a fast path

Joachim Smith20:06:40

so into is generally faster?


no, because vec now uses IReduceInit under the covers


But I'd stop obsessing over faster, make the code clear first

Joachim Smith20:06:44

unfortunately faster is better

Joachim Smith20:06:25

thanks everyone who helped out


I mean, if some code is in the hot spot of your system or program, and 10% differences there are important to you, measure a few variations and go for it. There are definitely some constructs in Clojure that make a much bigger difference than 10% on performance, and those are worth profiling for and improving -- where it actually matters to you.


neither of those thing is great for performance


building a new vector object from something else


like, if you don't do that at all, imagine how much faster things will be


Hi. Clojurians

👋 16

Hi. Trying to install Clojure on Win7. I changed the script to use "Tls" but now I get this error and I have tried some fixes but they didn't work.


@paulgureghian is your goal specifically to use the clojure / clj command line tools, or to use the clojure language?


because iirc with windows leiningen is much more mature, and it can load and run the clojure repl / compiler (as long as you have a jvm available)


@paulgureghian If you're determined to get the PowerShell alpha installer working on Windows 7 (which I think would be great), the #clj-on-windows channel will probably be a better place to ask for help since that's where the folks who've worked on that PS script hang out.


Can I do both ?


If you just want to try out Clojure on Windows 7, I agree with @noisesmith that Leiningen is probably going to be the easier path right now.


you can use both - but lein is more likely to just work out of the box, and using lein won't be a blocker to following tutorials you'll find online


whereas deps.edn (the basis for clj / clojure commands) is newer, and less documentation assumes you would be using it


Whats the url ?


you can download and run lein.bat


Is it like a modern distribution of Clojure ?


it's a package manager that assumes you are going to run clojure


you can use the latest clojure version with lein, and it knows how to pull in other libraries, run tests, create a standalone jar for distribution, etc.


clojure itself is a java library, and lein uses a config file to arrange some set of dependencies from a cache, and start the process


Leiningen has been around for a long time so it's the most battle-tested option on Windows, right now.


I can use Clojure in the terminal and the language ?


right, that's what lein is for


(with a focus on configuring individual projects)


eg. once you install lein.bat you can use lein repl to get a clojure interactive repl


(in a terminal)


Using the language means using Atom or VSCode with the Clojure extension ?


there are extensions to atom and vscode to connect to nrepl, which is the network repl process lein starts


Yup. Thats what I have


yeah, that actually assumes you are using lein and wouldn't work with clj out of the box


I assume Clojure on Mac / Linux is better ?


somewhat - but since clojure is all java, and java is portable, it all just works once you get a jvm and lein running


(with some gotchas around eg. emacs which prefers to run under linux / mac, but it doesn't sound like you'd have that problem)


Ok. Lets try Lenin

leiningen 4

there's some cool things going on with clj / deps.edn but especially with windows, it's probably not the right starting place for a beginner


What is the right starting place ? lein ?


yes, use lein to start


I'll let ya know how it goes.


Will Oracle Java be ok ?


yes, as long as you use 1.7 1.8 or newer


The very latest Clojure (1.10) now requires Java 1.8 or higher.


but really you should probably use 1.11 if you can, right?


1.8 is still pretty solid, but for production use I expect 1.8 will start getting harder and harder to find support for. For personal use, no problem.