Fork me on GitHub
#beginners
<
2019-07-18
>
Danny Almeida00:07:23

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"]'

seancorfield02:07:52

clojure.exe -- are you trying to run this on Windows?

seancorfield02:07:35

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).

Danny Almeida02:07:14

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 😞

John Collins03:07:03

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.

seancorfield04:07:48

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.

seancorfield04:07:56

@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.

seancorfield04:07:38

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.

Danny Almeida04:07:38

@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

Danny Almeida04:07:54

when things work..it's a big relief 🙂

seancorfield04:07:15

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.

seancorfield04:07:46

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.

Danny Almeida04:07:26

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 🙂

seancorfield04:07:29

@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 😞

seancorfield04:07:51

@dionysius.almeida What editor(s) are you used to, before Clojure?

Danny Almeida04:07:56

are there any good resources for moving to atom based workflow ?

Danny Almeida04:07:17

i started learning emacs just because of clojure

Danny Almeida04:07:52

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 🙂

seancorfield04:07:00

neovim or vim?

Danny Almeida04:07:09

just plain vim

seancorfield04:07:25

Did you look at fireplace?

Danny Almeida04:07:29

like you, my last experience on emacs was some two decades back

Danny Almeida04:07:51

sorry..but what's fireplace ?

seancorfield04:07:28

Fireplace is a plugin for vim.

Danny Almeida04:07:56

ah . i use vim mostly for just plain text editing ..nothing fancy

seancorfield04:07:05

(I know nothing about it -- I'm not much of a vim user -- but it's the de facto standard for vim I think?)

seancorfield04:07:20

For neovim, there's Conjure and a few others.

Danny Almeida04:07:32

i'll look into it .. thank you 🙂

seancorfield04:07:42

There's also Liquid, which is an in-process editor that is a lot like vim -- I like that.

Danny Almeida04:07:06

what would you recommend for mainly clojure/clojurescript workflow ?

Danny Almeida04:07:24

i like something which has configurable keybindings

Danny Almeida04:07:29

that's why i am using emacs

Danny Almeida04:07:40

i prefer using keyboard to mouse

seancorfield04:07:46

I don't do any cljs so I can't speak to that.

Danny Almeida04:07:49

i'm old school i guess 🙂

seancorfield04:07:08

But I know cljs folks who use Atom (and Chlorine is written in cljs!)

seancorfield04:07:05

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).

seancorfield04:07:59

I work almost entirely from the keyboard with Atom/Chlorine, FWIW.

Danny Almeida04:07:42

i think i'm going to spend this weekend familiarising myself with atom/chlorine 🙂

Danny Almeida04:07:52

thank you for the suggestion

John Collins04:07:56

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 🤞

Danny Almeida04:07:22

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 🙂

John Collins04:07:33

The built-in debugger is something to experience for sure

Danny Almeida04:07:08

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

Danny Almeida04:07:25

so when I launch emacs ..it just starts where I left off

Danny Almeida04:07:59

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

John Collins04:07:32

Taking it all on at once is brutal.

Danny Almeida04:07:07

i agree.. emacs+cider+clojure+clojurescript+lein+figwheel

John Collins04:07:16

Java + Clojure + Lisp + Emacs = Not Friendly

Danny Almeida04:07:16

that's a long list of things to get right 🙂

Danny Almeida04:07:45

couldn't agree more

seancorfield04:07:51

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...

John Collins04:07:11

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.

Danny Almeida04:07:55

mine is opposite...vim => spacemacs => emacs 🙂

John Collins04:07:54

Well sorta similar actually. I'm working on that final transition.

Danny Almeida05:07:38

i think spacemacs is a good place to start especially if one is coming from a vi/vim background

Danny Almeida05:07:44

smaller learning curve

John Collins05:07:29

For sure. Spacemacs is quite an achievement, although the project is out of control. It has 2600 open issues lol

John Collins05:07:56

Exuse me, 2200

Danny Almeida22:07:49

oh...that sucks

Danny Almeida00:07:33

that's the command cider is trying to run

bartuka01:07:30

I can't find a way to hide a endpoint on Swagger when using compojure-api, any thoughts?

John Collins01:07:19

@dionysius.almeida I recommend not using cider-jack-in and instead connect to an external repl via. cider-connect

Danny Almeida01:07:08

Thank you. I think I'll follow you advice and launch the repl from a terminal

John Collins01:07:58

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"]}

✔️ 8
John Collins01:07:28

@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.

bartuka03:07:43

@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"}))

John Collins03:07:39

Ah, good to know

ikitommi06:07:32

@iagwanderson try adding :no-doc true to route meta

💯 4
bartuka12:07:52

Thanks. I had found this on the swagger integration wiki documentation

bartuka12:07:18

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?

bartuka12:07:30

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 ;)

Kari Marttila10:07:19

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?

Kari Marttila10:07:39

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.

Kari Marttila10:07:12

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?

donaldball11:07:09

Datomic free is very capable, providing all the interesting local features of datomic against either an in-memory impl or a file-backed impl.

Kari Marttila12:07:23

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

donaldball13:07:52

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

Kari Marttila10:07:05

Damn. So much interesting things to learn and do but so little time - life is too short.

4
Kari Marttila11:07:34

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!

Kari Marttila12:07:16

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?

Alex Miller (Clojure team)12:07:01

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

Kari Marttila13:07:06

Thanks! I think about that.

FiVo13:07:51

Both lein jar and lein uberjar are blocking on a project of mine. Does this usually mean I am doing some blocking call somewhere?

noisesmith16:07:43

Yes, it's always a side effect inside def or a top level call that creates some service in my experience

Alex Miller (Clojure team)13:07:30

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)

Kari Marttila13:07:30

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!

28
🕺 28
4
Kari Marttila13:07:55

Clojure REPL really rocks!

parrot 48
😎 32
FiVo14:07:40

Ok, so i am doing AOT compilation and am doing (run-jetty handler {:port port :join? false}). Is this a problem?

jumar15:07:12

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

FiVo16:07:06

Yes it's wrapped in my main.

FiVo16:07:35

I commented it out and the problem persists, so it's something else.

jumar05:07:43

Probably some top-level def initializing some kind of thread-pool or something similar - hard to tell without the code

FiVo08:07:22

@U06BE1L6T So I figured it out, but I don't know why that was blocking. I had added target to :resource-paths of t

FiVo08:07:17

of the project, because that is necessary for figwheel-main. Once I moved that to the :dev profile it worked.

jumar08:07:53

hmm, good to know 🙂

FiVo14:07:10

Here is the dump if it helps: https://pastebin.com/raw/3aWJnwwc

thumbnail16:07:52

Anyone know of a tree-seq which doesn’t mapcat? E.g. not for 0..* relations but just 0..1 ?

thumbnail16:07:13

obviously I could use tree-seq with a funky children option.

thumbnail16:07:19

(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 approach

noisesmith16:07:20

You don't want a tree, you want a list

thumbnail16:07:27

Well, the data is pulled from datomic and i’m transforming it into a list

thumbnail16:07:04

the eventual data i need is basically [{:id 4} {:id 3} {:id 2} {:id 1}]

noisesmith16:07:03

(fn tree->seq [node] (when node (lazy-seq (cons (dissoc node :parent) (tree->seq (:parent node))))))

noisesmith16:07:21

Something like that should do it

noisesmith16:07:17

On mobile so I can't run it though

thumbnail16:07:52

creating a proper function does seem like the right approach, and yours looks pretty good. thanks 🙂

noisesmith17:07:05

OMG - I can't believe my parens were balanced on the first try from slack mobile :P

noisesmith17:07:08

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})

noisesmith17:07:58

if only this was a viable party trick :D

thumbnail17:07:25

Haha yeah it was spot on

Mario C.19:07:31

can you destructure a % in an anonymous function?

seancorfield19:07:05

as in (let [{:keys [a b]} %] ... a b ...) ?

dpsutton19:07:49

there's @john’s library that does things like this. Some love it, others consider it a solution in search of a problem

noisesmith19:07:25

using #() to generate a vector is already awkward

noisesmith19:07:53

I think having to add a let removes the cleanness that #() introduced

noisesmith19:07:18

one may as well use fn at that point

Mario C.19:07:57

I have a vector of vectors, that I will convert into a map but would like to convert the string keys to keywords.

Mario C.19:07:08

And yea I am using fn instead

noisesmith19:07:19

why would it require that? string keys are fine

Mario C.19:07:30

I do that mapping and then I call (apply array-map mapped-array)

noisesmith19:07:46

@mario.cordova.862 I'd use a transducer for that: (into {} (map (fn [[k v]] [(keyword k) v])) entries)

noisesmith19:07:06

(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}

noisesmith19:07:38

note that map isn't called on entries, it's passed to into as a transform to apply to each entry

noisesmith19:07:06

it skips the creation of a lazy-seq nobody needs

Mario C.19:07:15

That wont work since into doesn't preserver order

noisesmith19:07:31

nor does array-map once you call conj

noisesmith19:07:49

I guess that works if you're careful, but it's brittle

Mario C.19:07:04

what do you mean once I call conj?

noisesmith19:07:21

if you conj anything to an array map with over N items, it is replaced by a hash-map

noisesmith19:07:52

or more accurately, conj returns a hash map (of course the array-map is immutable and unaltered)

hiredman19:07:00

Use for and not map

Mario C.19:07:47

After the (apply array-map mapped-array) I am also doing (hash-map prev-key (apply array-map mapped-array))

Mario C.19:07:57

Use a for to turn the strings into keywords?

Mario C.20:07:56

I have (apply array-map (flatten (for [[k v] pr-terms] [(keyword k) v]))) and it seems to be working

andy.fingerhut20:07:33

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

Mario C.20:07:03

So the ordered map I have is purely coincidental?

andy.fingerhut20:07:15

it is deterministic, up to about 8 keys or so 🙂

Mario C.20:07:37

using into and zipmap I can tell right away no order is preserved

Mario C.20:07:56

But with this method it appears the order remains and I have more than 8 keys

andy.fingerhut20:07:20

You are welcome to play with the fire. I am showing you where it burns 🙂

Mario C.20:07:48

hmm then is there anyway I can keep the order?

andy.fingerhut20:07:02

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}

andy.fingerhut20:07:45

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.

andy.fingerhut20:07:41

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.

Mario C.20:07:15

(apply array-map (flatten (for [i (range 40)] [(keyword (str "test-" i)) (- i)])))

Mario C.20:07:25

(into (array-map) (for [i (range 40)] [(keyword (str "test-" i)) (- i)]))

Mario C.20:07:37

The second does not keep order but the first one does.

Mario C.20:07:32

I mean it makes sense to me. Wouldn't the fastest way to traverse a list be from beginning to end?

andy.fingerhut20:07:30

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.

andy.fingerhut20:07:49

When maps are small, a linear traversal of at most 8 keys is very fast, and takes little memory.

ghadi20:07:57

Maps have no order

ghadi20:07:18

if you accept that fact you can sleep peacefully

💯 4
andy.fingerhut20:07:26

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.

Mario C.20:07:40

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

andy.fingerhut20:07:32

I didn't say you did. I am explaining why most Clojure maps do not remember the insertion order.

Mario C.20:07:05

Fair enough. But I don't think that the code above is an accident. I think it works exactly as is intended

andy.fingerhut21:07:51

Agreed that your apply array-map expression is working exactly as intended.

andy.fingerhut21:07:34

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.

andy.fingerhut21:07:25

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.

Mario C.21:07:41

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.

Mario C.21:07:33

I am not doing anything with that map in my app where order matters

andy.fingerhut22:07:53

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.

Mario C.22:07:34

Should I add an extra step and always sort it?

Mario C.22:07:38

(into (sorted-map-by #(< (.indexOf term-names %1) (.indexOf term-names %2)))
                            (mapv (fn [term] {(keyword (:name term)) term}) terms))

andy.fingerhut22:07:25

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.

andy.fingerhut22:07:49

but that might not be very useful if the map with the order requirements is nested deeply inside of other data.

andy.fingerhut22:07:48

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.

andy.fingerhut22:07:29

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.

Mario C.23:07:17

At will be less than 100. But we ended up having a discussion and we are not sorting anything anymore.

Mario C.23:07:55

Besides the other team shouldn't be relying on our sort.

andy.fingerhut23:07:50

Sounds like a win all around, if they can avoid relying on a particular order

Mario C.20:07:38

You are right that if I do assoc another key to it a new map, out of order, will be returned.

John22:07:39

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.

noisesmith22:07:48

the :as all should be inside the destructure map

noisesmith22:07:02

you are binding x to (:a :as) which is nil

John22:07:34

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.

noisesmith22:07:20

you put the :as :all inside the destructure map

noisesmith22:07:52

(let [{x :a :as all} {:a 10 :b 20}] x)

John22:07:30

Oh, excellent, thank you very much!