This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-07-18
Channels
- # aleph (7)
- # announcements (11)
- # beginners (186)
- # calva (17)
- # cider (26)
- # clj-kondo (4)
- # cljdoc (12)
- # cljs-dev (3)
- # clojars (1)
- # clojure (105)
- # clojure-berlin (1)
- # clojure-chicago (1)
- # clojure-dev (34)
- # clojure-europe (3)
- # clojure-italy (4)
- # clojure-nl (27)
- # clojure-russia (19)
- # clojure-uk (25)
- # clojuredesign-podcast (4)
- # clojurescript (54)
- # cursive (6)
- # data-science (1)
- # datascript (11)
- # datomic (5)
- # emacs (3)
- # events (2)
- # fulcro (13)
- # graalvm (5)
- # jobs (15)
- # leiningen (7)
- # luminus (3)
- # melbourne (1)
- # nrepl (1)
- # nyc (2)
- # onyx (4)
- # pathom (6)
- # pedestal (18)
- # re-frame (19)
- # reagent (10)
- # shadow-cljs (27)
- # spacemacs (32)
- # sql (11)
- # tools-deps (35)
- # vim (50)
clojure.exe" -Sdeps '{:deps {nrepl {:mvn/version "0.6.0"} refactor-nrepl {:mvn/version "2.5.0-SNAPSHOT"} cider/cider-nrepl {:mvn/version "0.21.1"}}}' -m nrepl.cmdline --middleware '["refactor-nrepl.middleware/wrap-refactor", "cider.nrepl/cider-middleware"]'
clojure.exe
-- are you trying to run this on Windows?
If so, I'm pretty sure you won't be able to get that to work due to strange shell quoting issues on Windows (both cmd
and Powershell). You'll have to do what @collins recommended and start the REPL manually (which I think is a better workflow anyway since you can interact with the REPL directly as well as connecting multiple clients to the same REPL if needed).
Yes, i'm running this on windows. Thanks..i think i'll stick to lein as that works out of the box with cider. Also, when I start repl from command line and then connect using cider-connect, it gives out of sync warnings ..not sure how to deal with that 😞
I started with lein as well, but have say I'm very happy I switched over to tools.deps. idk how but the the startup time for me using deps is wildly faster than lein.
lein
starts two JVMs. clj
uses one JVM to figure out dependencies, fetch them, and caching the classpath -- if needed -- and then a JVM to run Clojure with everything already set up. So you pay a (smaller-than-`lein`) price to set up the dependencies once and then a much-smaller-than-`lein` cost to actually run code.
@dionysius.almeida If you're getting "out of sync" warnings, just update the dependencies so you're starting the same versions at the command line as CIDER expects.
Although, frankly, this is part of why I stopped using CIDER and nREPL and all that stuff -- so many things need to align for it all to work. There's just far too many moving parts and far too many variables.
@seancorfield I'm still a learner ..so getting to know all the kinks in various tooling options 🙂 Sometimes it can be really frustrating as the error messages don't exactly help .. this is one area where I wish something can be done..the stack traces are pretty much useless in trying to figure out what's going on
when things work..it's a big relief 🙂
I got tired of Emacs breaking almost any time I updated any packages. I got tired of CIDER/nREPL breaking every time there was an update. I used Emacs 20+ years ago and came back to it when I started doing Clojure. I switched from Emacs to Atom/ProtoREPL in 2015 (still using nREPL) and then late last year I switched to Atom/Chlorine and a plain Socket REPL -- and I love that! I can start up any Clojure process and with JVM options can tell it to start a Socket REPL -- and then connect Atom/Chlorine to it and work live.
Today I worked with Atom on my desktop connected to a Socket REPL in a process on a remote server to debug a complex problem and apply live updates to the process while it was running. You can't do that with CIDER/nREPL.
I kinda like emacs as i've got used to it 🙂 ..but I will look into atom as I am really spending more time trying to get all the stars to align than doing actual work 🙂
@dionysius.almeida That's part of the problem with lein
/ emacs / cider / nrepl -- too much magic and too many moving parts: it's great when it works but it's terrible for a beginner when it goes wrong 😞
@dionysius.almeida What editor(s) are you used to, before Clojure?
are there any good resources for moving to atom based workflow ?
i started learning emacs just because of clojure
spent a good deal of time configuring emacs to get to a stage where I am happy..but then the clojure tooling is now the sore point 🙂
neovim or vim?
just plain vim
Did you look at fireplace?
like you, my last experience on emacs was some two decades back
sorry..but what's fireplace ?
Fireplace is a plugin for vim.
ah . i use vim mostly for just plain text editing ..nothing fancy
(I know nothing about it -- I'm not much of a vim user -- but it's the de facto standard for vim I think?)
For neovim, there's Conjure and a few others.
i'll look into it .. thank you 🙂
There's also Liquid, which is an in-process editor that is a lot like vim -- I like that.
what would you recommend for mainly clojure/clojurescript workflow ?
i like something which has configurable keybindings
that's why i am using emacs
i prefer using keyboard to mouse
I don't do any cljs so I can't speak to that.
i'm old school i guess 🙂
But I know cljs folks who use Atom (and Chlorine is written in cljs!)
For a while, when Chlorine was just getting started, I worked from source, so I ran shadow-cljs and used Chlorine from source with Atom, and modified the cljs source (and when I saved a file it auto-recompiled and reloaded into Atom so changes were live immediately).
I work almost entirely from the keyboard with Atom/Chlorine, FWIW.
i think i'm going to spend this weekend familiarising myself with atom/chlorine 🙂
thank you for the suggestion
Cider and emacs was a seriously rough slog for me for a while (I would never have stuck with it if I wasn't already committed to emacs), but it does seem to have gotten substantially better recently. I am now happy with it 🤞
to be honest, i've spent more time configuring emacs to get to a state where I'm finally happy ..but then the struggle with clojure tooling kinda makes me think , i could have better used those hours on coding clojure 🙂
The built-in debugger is something to experience for sure
with my emacs setup i get desktop save and restore to the state i last left..including restoring frames and buffers in the state they were in
so when I launch emacs ..it just starts where I left off
took some time to get it to work ..as I'm a complete newbie when it comes to lisp and had no prior experience with emacs lisp
Taking it all on at once is brutal.
i agree.. emacs+cider+clojure+clojurescript+lein+figwheel
Java + Clojure + Lisp + Emacs = Not Friendly
that's a long list of things to get right 🙂
lol yeah
couldn't agree more
When I first started with Clojure, I used TextMate and a bare bones REPL. I used a wide variety of editors over the years (since I started in 2010!) and it took me a lot of time to get productive with Emacs again. But it took a lot less time to get productive with Atom so...
My editing evolution has been Emacs => Vim => Evil Emacs (spacemacs). I was shocked to find emacs offered such a solid vim editing experience. I've diverged from spacemacs a lot and am slowly phasing it out, but definitely did not "get" emacs prior to that.
mine is opposite...vim => spacemacs => emacs 🙂
Well sorta similar actually. I'm working on that final transition.
i think spacemacs is a good place to start especially if one is coming from a vi/vim background
smaller learning curve
For sure. Spacemacs is quite an achievement, although the project is out of control. It has 2600 open issues lol
Exuse me, 2200
oh...that sucks
that's the command cider is trying to run
I can't find a way to hide a endpoint on Swagger when using compojure-api
, any thoughts?
@dionysius.almeida I recommend not using cider-jack-in and instead connect to an external repl via. cider-connect
Thank you. I think I'll follow you advice and launch the repl from a terminal
I use something like the following alias to launch an nrepl:
{:extra-paths ["resources" "src/clj"]
:extra-deps {refactor-nrepl {:mvn/version "2.4.0"}
org.clojure/tools.nrepl {:mvn/version "0.2.12"}
cider/cider-nrepl {:mvn/version "0.22.0-beta1"}
nrepl {:mvn/version "0.6.0"}
}
:main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]
:jvm-opts ["-Xmx6g"]}
@iagwanderson, did you come across this? https://stackoverflow.com/questions/29701573/how-to-omit-methods-from-swagger-documentation-on-webapi-using-swashbuckle looks like you can add a property to Controllers. Also yada has very good swagger support you might want to check out.
@collins I found a solution, I just needed to add :no-doc true
to my route
(GET "/something" []
:no-doc true
:summary "endpoint"
(ok {:msg "go back"}))
Ah, good to know
There is something else I wish to know about compojure-api
. In order to publish it in production, there are any additional recommendations? I'm setting up the component
library with ring-jetty server. Is that enough, necessary or bad idea?
There are now 3 services in clojure that are going into production in the next few weeks. I want to make it more robust with tests and they are ready to go. A very nice move forward since last time we spoke ;)
Hi fellow clojurists! I'm back. I had to spend a couple of months studying and doing two GCP certifications (corporation request) but now I can study again what ever I like in the evenings - and I like to study Clojure. 🙂 I have been reading "Mastering Clojure" lately and I must say that it is a bit challenging. Anyone else read the book? How would you compare it to other Clojure books?
BTW. I'm using Cognitect's AWS library with one personal Clojure learning project. Next I'd like to do the same exercise using GCP Datastore. Any suggestions for libraries? Short googling says that maybe I need to use it via the Datastore Java library.
Another idea would be to use Datomic. I guess there is no "local Datomic Docker image" (as for DynamoDB) that would provide the same API as the real Datomic that you could use for experimentation? So, the only way is to use the paid 1$/day AWS version?
Datomic free is very capable, providing all the interesting local features of datomic against either an in-memory impl or a file-backed impl.
All right! Good to know! I experiment with Datomic free then. Short googling gave me this documentation: https://docs.datomic.com/on-prem/dev-setup.html
FWIW you don’t even need to do all that. If you include the datomic free lib in your dependencies, in your app you can use datomic:
as the uri for a non-durable instance, perfectly adequate for learning
Damn. So much interesting things to learn and do but so little time - life is too short.
https://clojutre.org/2019/ => Alex Miller is going to be there. This time I really need to attend and bring my copy of "Programming Clojure" to get an author signed book. Yihaa!
Damn. I just got a great idea! In my previous version of one Clojure learning server I implemented different Leiningen profiles for local development and real AWS DynamoDB as a database (and Azure and ...). I'm re-implementing the server with Cognitect's new AWS library and also experimenting to use deps.edn. I figured out a nice way how to use Mount to reset application state so that I can switch the backend from local file to real AWS DynamoDB. But switching requires some editing in some configuration files (that Mount reads). I just got an idea. How about if I use some dynamic variable, e.g. dev-db and I can dynamically give some value to this variable in repl (e.g. "local" or "real-aws"). And then reset Mount and while Mount is reloading the configuration it first checks this dynamic variable's value, and if it is nil, then reads the configuration from the configuration file. But if the dynamic variable value is something else than nil, then I use either hard-coded local map configuration or "real-world" aws configuration => this way I can switch dynamically in the repl by changing the dev-db variable (and resetting Mount) to any backend db I want during development. Is this a typical use case for such a dynamic variable in Clojure?
Well, even better imo is to not use a dynamic variable at all and just pass the instance you want to use to the app
Thanks! I think about that.
Both lein jar
and lein uberjar
are blocking on a project of mine. Does this usually mean I am doing some blocking call somewhere?
Yes, it's always a side effect inside def or a top level call that creates some service in my experience
you may be able to do ctrl-\ to thread dump and find out (or kill -3 the pid if it's not your fg process)
Clojure is so much fun! I'm already 53 years old and when learning Clojure and trying new Clojure things every day I feel like a little boy with a bunch of techno legos - what marvellous things to build today? Yihaa!
Ok, so i am doing AOT compilation and am doing (run-jetty handler {:port port :join? false})
. Is this a problem?
If it's top-level form then that's weird. you should wrap it into a function and call it only when your app is starting
join? false
shouldn't block the main thread but I guess the jetty server's thread(s) will prevent JVM from exiting
Probably some top-level def
initializing some kind of thread-pool or something similar - hard to tell without the code
@U06BE1L6T So I figured it out, but I don't know why that was blocking. I had added target
to :resource-paths
of t
of the project, because that is necessary for figwheel-main
. Once I moved that to the :dev
profile it worked.
Here is the dump if it helps: https://pastebin.com/raw/3aWJnwwc
Anyone know of a tree-seq
which doesn’t mapcat
? E.g. not for 0..* relations but just 0..1 ?
(let [data {:id 4, :parent {:id 3, :parent {:id 2, :parent {:id 1}}}}]
(tree-seq :parent #(vector (:parent %)) data))
=>
({:id 4, :parent {:id 3, :parent {:id 2, :parent {:id 1}}}}
{:id 3, :parent {:id 2, :parent {:id 1}}}
{:id 2, :parent {:id 1}}
{:id 1})
This is an example; just curious if there’s a more clear approachYou don't want a tree, you want a list
(fn tree->seq [node] (when node (lazy-seq (cons (dissoc node :parent) (tree->seq (:parent node))))))
Something like that should do it
On mobile so I can't run it though
creating a proper function does seem like the right approach, and yours looks pretty good. thanks 🙂
OMG - I can't believe my parens were balanced on the first try from slack mobile :P
nothing but net 🗑️
ustin.smith@C02RW05WFVH6: /tmp/foo$ clj
Clojure 1.10.0
(ins)user=> (fn tree->seq [node] (when node (lazy-seq (cons (dissoc node :parent) (tree->seq (:parent node))))))
#object[user$eval136$tree__GT_seq__137 0x13e547a9 "user$eval136$tree__GT_seq__137@13e547a9"]
(ins)user=> (def tree->seq *1)
#'user/tree->seq
(ins)user=> (let [data {:id 4, :parent {:id 3, :parent {:id 2, :parent {:id 1}}}}] (tree->seq data))
({:id 4} {:id 3} {:id 2} {:id 1})
if only this was a viable party trick :D
as in (let [{:keys [a b]} %] ... a b ...)
?
there's @john’s library that does things like this. Some love it, others consider it a solution in search of a problem
@seancorfield Yea that works
using #() to generate a vector is already awkward
I think having to add a let removes the cleanness that #() introduced
one may as well use fn at that point
I have a vector of vectors, that I will convert into a map but would like to convert the string keys to keywords.
why would it require that? string keys are fine
n/m misread
@mario.cordova.862 I'd use a transducer for that: (into {} (map (fn [[k v]] [(keyword k) v])) entries)
(ins)user=> (def entries [["a" 0] ["b" 1] ["c" 2]])
#'user/entries
(ins)user=> (into {} (map (fn [[k v]] [(keyword k) v])) entries)
{:a 0, :b 1, :c 2}
note that map isn't called on entries, it's passed to into as a transform to apply to each entry
it skips the creation of a lazy-seq nobody needs
nor does array-map once you call conj
I guess that works if you're careful, but it's brittle
if you conj anything to an array map with over N items, it is replaced by a hash-map
or more accurately, conj returns a hash map (of course the array-map is immutable and unaltered)
After the (apply array-map mapped-array)
I am also doing (hash-map prev-key (apply array-map mapped-array))
I have (apply array-map (flatten (for [[k v] pr-terms] [(keyword k) v])))
and it seems to be working
If you want a map, and you want it to preserve insertion order, array-map only does that for about up to 8 keys or so, then it switches implementation to a hash map that forgets the insertion order
it is deterministic, up to about 8 keys or so 🙂
You are welcome to play with the fire. I am showing you where it burns 🙂
user=> (into (array-map) (for [i (range 5)] [i (- i)]))
{0 0, 1 -1, 2 -2, 3 -3, 4 -4}
user=> (into (array-map) (for [i (range 10)] [i (- i)]))
{0 0, 7 -7, 1 -1, 4 -4, 6 -6, 3 -3, 2 -2, 9 -9, 5 -5, 8 -8}
You can choose to maintain a separate sequential data structure, e.g. a vector, in addition to your map, so that the vector remembers the order you care about and the map gives fast lookup by key.
Or you can use a library that bundles those things together for you, e.g. https://github.com/clj-commons/ordered contains ordered-map and ordered-set data structures. Under the hood they maintain a vector and a map, and keep them in sync with each other.
I mean it makes sense to me. Wouldn't the fastest way to traverse a list be from beginning to end?
The fastest way to look up a key in a set of 10,000 keys is often NOT by traversing it from beginning to end, and Clojure maps are intended to be optimized for fast lookup of arbitrary keys.
When maps are small, a linear traversal of at most 8 keys is very fast, and takes little memory.
The fact that small maps preserve key insertion order, and that apply array-map
does, should be considered an accident, not a promise to rely upon. If you take the result of apply array-map
expression and assoc one more new key onto it, it will return a new map with a different order.
To be fair I never said the fastest way to look up a key in a set of 10K keys was to traverser from beginning to end
I didn't say you did. I am explaining why most Clojure maps do not remember the insertion order.
Fair enough. But I don't think that the code above is an accident. I think it works exactly as is intended
Agreed that your apply array-map
expression is working exactly as intended.
It is also a very atypical way to create a map, and such a map passed to nearly any other operation on maps, e.g. assoc, merge, select-keys, etc. is not guaranteed to preserve that ordered property in the returned value. Very, very brittle.
If you want guaranteed insertion order to be preserved, and you rely upon only array-map, you will likely feel very very restricted in the operations you can do on such a map, i.e. almost none.
I agree it is very brittle. If it were up to me the order would not matter. Unfortunately another app the business uses hits our API and their program expects the response to be laid out in a certain order otherwise their operation breaks.
if the order matters, and this code might some day be updated to meet new requirements, it seems you may be doing a favor to that future maintainer (perhaps you) about this requirement, and how no operations should be done on the result of the array-map operation, or else the program will no longer meet its requirements.
(into (sorted-map-by #(< (.indexOf term-names %1) (.indexOf term-names %2)))
(mapv (fn [term] {(keyword (:name term)) term}) terms))
Is the map being sent back in some direct way, or is it being traversed in order to print out its key/value pairs somehow? If the latter, and if you have the order of terms you care about in term-names
, you could simply iterate through term-names
when printing out keys/values.
but that might not be very useful if the map with the order requirements is nested deeply inside of other data.
The particular sort function you give in your code snippet above might get prohibitively slow if term-names
grows very large. If you know it is at most 50 to 100 elements or so, then maybe no big deal.
You could speed that up by pre-calculating a map from term-names
to their .indexOf values, and look up term-name values in that map in the comparison function.
At will be less than 100. But we ended up having a discussion and we are not sorting anything anymore.
Sounds like a win all around, if they can avoid relying on a particular order
You are right that if I do assoc another key to it a new map, out of order, will be returned.
Is there an easy way to put names to the intermediate results that you destructure? Writing (let [{x :a} {:a 10 :b 20}] x)
correctly returns 10, but whenever I add an :as
like this (let [{x :a} :as all {:a 10 :b 20}] x)
I strangely get a nil as a result (while the value of all
is correct: {:a 10 :b 20}
, I just don't use that value for the sake of this example, but I would use it in reality). For sure there must be something I'm doing wrong.
the :as all should be inside the destructure map
you are binding x to (:a :as)
which is nil
Okay, thank you, that makes sense, and how would I go about naming the entire object that I'm trying to destructure? I mean, I would like x
to be 10
and all
to be {:a 10 :b 20}
. And writing something like (let [({x :a} :as all) {:a 10 :b 20}] x)
is not valid syntax.
you put the :as :all inside the destructure map
no parens
(let [{x :a :as all} {:a 10 :b 20}] x)