Fork me on GitHub
#clojure
<
2022-04-25
>
Steffen Glückselig06:04:04

I just sumpled over nrepl/drawbridge 0.2.1 (nREPL over http(s)) not working with Leiningen 2.9.5 doing lein repl :connect . It hangs with the message Connecting to nREPL at - the server gets masses of requests, though. I tried this in the project.clj:

(defproject gungfu/mini-app "0.1.0-SNAPSHOT"
  :plugins [[nrepl/drawbridge "0.2.1"]]
  :managed-dependencies [[nrepl "0.6.0"]])
Or has an alternative to drawbridge emerged?

practicalli-johnny10:04:51

Clojure now supports a socket REPL https://clojure.org/reference/repl_and_main (scroll down to section on socket server) Not quite the same as drawbridge, but maybe of use

Shuky Badeer08:04:37

Hi guys! Using next.jdbc query in clojure, I get results of the format down below. Now my question is how do i access the data inside #:tenant? There must be a straightforward way to do this. What am i not understanding? Thank you!

J08:04:37

The result of your next.jdbc query is a namespaced map. To access the data, you can: (:tenants/name result)or (get result :tenants/name) More informations on https://clojure.org/reference/reader#_maps

👍 1
Shuky Badeer08:04:19

Thank you. That works fine but is there a way to simply get all the content at once without the namespace?

Shuky Badeer08:04:58

Like i can write a function that manipulates the data but sounds like a pretty basic thing to do

Shuky Badeer09:04:59

You're the best! Thank you!

dharrigan09:04:13

@UHZPYLPU1 there is a very strong argument for keeping the namespace, which manifests when doing joins. If you join a table to another table (and those tables have similar column names), then unless you take preventative measures yourself, then there is no guarantee that the value of the column will contain the value you want.

☝️ 1
1
dharrigan09:04:37

Using namespaced keys prevents that, in addition, it's quite easy to destructure the data, i.e.,

1
dharrigan09:04:26

(let [{:tenants/keys [created_at name id deleted_at]} tenants] (println created_at name id deleted_at))....

1
dharrigan09:04:46

It's strongly encouraged in the documentation to try to keep the namespaces 🙂

1
J09:04:30

@U11EL3P9U I agree with you 🙂

1
dharrigan09:04:15

@U033V0AJFU4 namespaces are good 🙂

1
cjohansen10:04:28

Namespaces are good, but Clojure’s printing of “namespaced maps” are not, in my opinion. If you turn it off it is much easier to understand how to work with the data, and in my eyes easier to read:

(set! *print-namespace-maps* false)

cjohansen10:04:07

This just changes the visualization, so instead you’d see

{:tenants/created-at ,,,
 :tenants/name ,,,
 :tenants/id ,,,}

1
Shuky Badeer05:04:39

Will keep all that in mind! Thanks a lot guys!

Nom Nom Mousse10:04:46

Is the something like mapcat but for for? I think (apply concat (for is clunky.

magnars10:04:43

I have this beauty in several codebases :thinking_face:

(defmacro forcat
  "`forcat` is to `for` like `mapcat` is to `map`."
  [& body]
  `(mapcat identity (for ~@body)))

🙏 1
p-himik10:04:21

At that point, I'd probably use reduce.

tatut10:04:18

why not just use mapcat ?

p-himik10:04:57

Because for has capabilities that mapcat does not.

tatut10:04:41

that's true

p-himik10:04:41

reduce is closer to mapcat than to for, but you'll be building a single collection, without anything intermediate. With either for or mapcat you will have either one intermediate lazy collection that will be thrown away or a whole bunch of them. But maybe I'm overthinking things, that happens too. :)

tatut10:04:46

how about common lisp loop macro if you really want to overkill

Ben Sless10:04:30

Use the return expression as another binding (for [x xs y (body ,,,)] y)

5
Nom Nom Mousse10:04:03

Thanks all 🙂

Nom Nom Mousse10:04:06

@Ben That is my favourite suggestion 🙂

🎉 1
noisesmith19:04:15

> how about common lisp loop macro if you really want to overkill did someone actually port loop to clojure, or do you mean clojure.core/loop?

noisesmith19:04:33

loop in common lisp is a DSL implementing a language that definitely isn't lisp

(loop with list-a = '(1 2 3)
      with infinite-list = (setf (cdr (last list-a)) list-a)
      for item in infinite-list
      repeat 8
      collect item)
;; (1 2 3 1 2 3 1 2)

🧠 1
zimablue13:04:22

does anyone ever feel like you should be able to send into go blocks? or can you and I just don't know the syntax? Like (def go-block-that-accepts-values (go (+ 3 %sent-in))) (go (println (<! (>! go-block-that-accepts-values 5)))) ;; prints 8 like go returns a channel but AFAIK not one that can be sent into, it feels a lot like a generator but without that one useful feature

Alex Miller (Clojure team)13:04:12

you can close over for a one-time thing, or you can give the go block a channel and send values to the channel

zimablue13:04:25

Thanks, I've used both of those before and they work fine. I guess if one allowed values to be pushed into go blocks, there has to be some thought put into buffering and orders, like I send a shitload of values, does it just block/explode? I send a value before it's expecting one, does it just hold it until it needs it? I send a value when it's never expecting one, does it throw?

Alex Miller (Clojure team)13:04:33

that's why we channels with a variety of buffer choices

zimablue13:04:40

go blocks are kind of implicitly buffer 1 right, they'll wait until each value is pulled out to continue

Alex Miller (Clojure team)13:04:26

go blocks don't have buffers at all

Alex Miller (Clojure team)13:04:56

are you referring to the output channel returned by a go block?

Alex Miller (Clojure team)13:04:28

if so, that's a channel with buffer of fixed size 1

zimablue13:04:30

that's what I meant, the output channel

Alex Miller (Clojure team)13:04:56

but you can also give a go block an explicit output channel that does whatever you need

zimablue14:04:15

Exactly, so to make input and output symmetric since they're implicitly buffersize 1 for output, being buffersize 1 for input would be logical

Alex Miller (Clojure team)14:04:05

they are symmetric - you can supply both to do whatever you want :)

zimablue14:04:59

yeah it's definitely not a concrete limitation I was just wondering whether there was another design choice that would allowed for slightly more succinct code

zimablue14:04:11

as you say it can be achieved by passing in the channel

didibus17:04:13

Putting into a go block doesn't really make logical sense.

didibus17:04:59

You should think of go as starting a sequential process. It is more akin to (Thread.)

didibus17:04:52

It's like that in a primitive way. And then you can if you want build higher level abstractions on top.

(defn adder-3
  [value]
  (go (+ 3 value)))

(go (println (<! (adder-3 10)))

Alex Miller (Clojure team)17:04:40

go is a machine, channels are conveyor belts

💯 1
Alex Miller (Clojure team)17:04:16

machines can take inputs or provide outputs on 0...N belts

didibus17:04:46

So to be clear, in your case, you can easily achieve what you want by just wrapping a go block in a function, like in my example code above.

didibus17:04:58

And to Alex Miller's point, that's often how you do it, just using a function around the go machine.

(defn some-go-process
  [input-chan-0 input-chan-1 ... input-chan-N
   output-chan-0 output-chan-1 ... output-chan-N]
  (go
     ;; Take from the input chans as needed
     ;; Put on the output chans as needed))
The implicitly returned channel from the GO block itself is best thought as a completion signal, it tells you when the process is done, so you can block/park or poll! it for completion. And the single result it contains could be details of what the process did or didn't do, errors, etc. Though you can also conveniently use it for process that return a single result as well, instead of taking an explicit output channel.

manutter5113:04:04

Ok, this surprised me: [EDIT: because I’m an idiot :P] [Code deleted, but basically I was surprised because I accidentally typed in my data as a set, thinking I was typing it as a map, and was surprised that my “keys” and “values” came out mixed up.]

manutter5113:04:52

Notice that for the second line, the key is [2nd item of a pair] and the value is [first item of the pair] instead of the other way around.

magnars13:04:41

#{} creates a set, which is not ordered. I'm guessing you wanted {}

manutter5113:04:48

Oh, derp, that was it. I’m getting schema data from datomic, and pulling out bits of it to generate mock data for tests, and somehow the extra # slipped in while I was editing.

😊 1
manutter5114:04:23

Aha, I figured out where I got myself confused:

#:foo{:abraham 1
      :martin 2
      :john 3}

;; Need to add a new key from the :bar/ ns, so move the `:foo` inside the curly braces

#{:foo/abraham 1
  :foo/martin 2
  :foo/john 3
  :bar/puff 4}

dpsutton14:04:48

just want to point out you can leave the prefix style and use other namespaces or even no namespaces

#:foo{:abraham 1
      :martin 2
      :john 3
      :bar/puff 4
      :_/no-namespace 5}

👍 2
Joshua Suskalo16:04:33

so how do you refer to the namespace called _?

Joshua Suskalo16:04:36

do you just not?

dpsutton16:04:20

i was thinking about that. its a legal keyword :_/foo. Not sure if it is expressable in this instance

Joshua Suskalo16:04:35

Yeah, seems to be the case

manutter5114:04:58

I moved the prefix inside the braces, but left the # in front, so my map is now a set, and I didn’t even notice.

magnars15:04:27

Another reason why I vastly prefer (set! *print-namespace-maps* false)

Darin Douglass15:04:39

ya, it’s one of the only objectively bad things in the language, imo

magnars15:04:50

Yeah, I think it was a mistake.

Alex Miller (Clojure team)16:04:13

It's actually the default

Alex Miller (Clojure team)16:04:35

The clojure.main repl overrides it

devn17:04:17

Anyone know if there’s anything better/more mature than clojail these days for sandboxing clojure exprs?

devn17:04:53

ah nice, i haven’t checked it out. use case: I am reviving an old project where I scraped a ton of s-expressions from the #clojure IRC channel’s logs, and then ran everything that looks like a valid sexp in a sandbox. i then captured the values and outputs of each of them, and made the result searchable

devn17:04:25

as of 1.5.1 I got to about ~32,000 working expressions, looking to make another pass on this with latest clojure

devn17:04:25

one thing i don’t immediately see in SCI is a notion of a timeout. plenty of expressions in the input set run for an infinite amount of time

phronmophobic20:04:34

I know that someone has tried using clojurescript + https://www.stopify.org/ for this purpose. Not sure how well it worked.

phronmophobic21:04:58

I know sci is sometimes mentioned as a sandbox tool, but I haven't seen anything that demonstrates what usage might be safe or demonstrate that "safe" usage exists. Since sci relies on an uninterpreted clojure.core library, I don't think it's realistically possible to prevent non-terminating programs or programs that consume arbitrary amounts of memory. Another approach is to set restrictions at the OS process level.

devn21:04:23

ill probably do both

Ben Sless17:04:30

Is there an alternative to using dynamic variables if I want to pass a state as part of a test fixture?

p-himik17:04:41

A couple of weeks ago I had the same question. After reading the source code of clojure.test, the relevant documentation, examples, and other materials, it seems that dynamic variables are the way to go.

Ben Sless17:04:40

I can always do something with macros (first rule of macro club)

devn21:04:00

is there ever any use in calling bounded-count on strings? my understanding is that strings are Counted, and thus O(1) for count

ghadi21:04:54

strings are not Counted

ghadi21:04:09

at least for the purpose of bounded-count, it falls back to the seq path

ghadi22:04:42

(I think alex was saying "correct" to your last clause @U06DQC6MA -- there's no need to call bounded-count on strings)

devn22:04:05

right, makes sense

andy.fingerhut04:04:41

clojure.core/count should be O(1) runtime on strings, but because clojure.lang.RT/count Java method has an O(1) implementation for it.

andy.fingerhut05:04:08

Yeah, but strings do not implement the Counted interface, so looks like bounded-count would be linear time on strings.