Fork me on GitHub
#clojure
<
2020-09-24
>
Clypto04:09:21

what's up with clj-kondo in vscode, it seems like it can't see dependencies and fill up files with unresolved symbols in the editor

practicalli-johnny18:09:52

I hope you are using the Calva extension for VSCode, there is another Clojure extension that is not as capable Recomend using https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva

practicalli-johnny17:09:50

hopefully the nice people on #calva can help you if you are still having issues.

seancorfield04:09:33

@clypto Probably a better question for #clj-kondo and/or #vscode / #calva ?

seancorfield04:09:01

(although it sounds like you haven't run the project initialization for clj-kondo per the README?)

Clypto04:09:03

maybe, but I'm not sure where said readme is

seancorfield04:09:22

Er, the clj-kondo readme?

Clypto04:09:44

ah, I was looking at the VS Code extension page

Clypto04:09:46

which is quite sparse

Clypto04:09:24

I think I had grand ideas on what the extension automatically did 😄

seancorfield05:09:21

Yeah, I had to blow away the .clj-kondo/.cache folder in one of my projects today and re-run the parallel lint initialization thing again because it got stuck thinking a couple of functions had different arities after some refactoring...

borkdude06:09:59

If you refactor using the same editor that should not happen, since it lints files when editing and the last refactored file has the last known valid arities

nickt12:09:50

Hey folks, I'm struggling a bit with variadic functions this morning– I have a function defined like:

(defn create-node [node-type props & children]
  ...blah)
And then I have a call site where I write (create-node "x" {}), and when I run the cljs compiler I get "wrong number of arguments passed to create-node"

nickt12:09:06

Why is that the wrong number of arguments? I want that call to be valid and for children, in that case, to be an empty list

manutter5112:09:00

I don’t remember the answer to your question about arity, but the “create-node” name reminds me of a bug I ran into where I used a variadic argument to pass in some child nodes, and it blew up as soon as I tried to create a node with a large number of children. There’s a limit on the number of arguments a Java method can accept, so just a caveat in passing.

nickt12:09:24

ok, thanks, that's definitely helpful

nickt12:09:52

I just tried taking the list of children manually instead of via a variadic parameter, and I'm hitting the same "wrong number of argugments" still

nickt12:09:08

create-node can call itself recursively... would that have anything to do with it?

manutter5112:09:21

Could be, depending on how it’s recursing

manutter5112:09:50

Your original defn looks correct to me, fwiw

manutter5112:09:33

What’s the exact text of the error? It should tell you how many args it was expecting vs how many it got.

nickt12:09:05

WARNING: Wrong number of args (3) passed to cljs.core/create-node at line 12

Ed12:09:12

It works for me ... Is create node recursive? Is there something in ...blah that's actually causing the error?

% clj --main cljs.main --repl
 ClojureScript 1.10.758
 cljs.user=> (defn create-node [node-type props & children] :blah)
 #'cljs.user/create-node
 cljs.user=> (create-node "x" {})
 :blah
 cljs.user=>

nickt12:09:24

and this is my new fn def:

(defn create-node [node-type props children]

manutter5112:09:48

Ok, that’s sounding like your REPL has stale code stuck in it somehow, can you restart?

borkdude12:09:49

@nickt It says cljs.core/create-node which seems wrong

manutter5112:09:09

oh, derp, yeah, I missed that, good catch

dpsutton12:09:24

That means need to refer clojure exclude create-node

manutter5112:09:48

or name your function something different

nickt12:09:52

ohhhh man, does clojure have its own create-node?

borkdude12:09:01

it seems cljs has

manutter5112:09:02

Apparently cljs at least does.

manutter5112:09:35

Try (doc cljs.core/create-node)

nickt13:09:53

oh mannnn

nickt13:09:15

wow, well thanks y'all. That would have taken me a long time to figure out 🙂

nickt13:09:25

(clojurescript newb)

nickt13:09:55

so how would I refer exclude it?

nickt13:09:03

also, side question, is there a good way to accept a variadic children or an explicit list of children?

nickt13:09:36

it seems my best bet would be to make that definition be [node-type props & children] but then check if the first element in children is itself a list. If it is, use that as my children array?

Alex Miller (Clojure team)13:09:01

I would recommend not doing that and just picking one or the other

Alex Miller (Clojure team)13:09:04

It’s just easier to understand and maintain

nickt13:09:51

@alexmiller thanks, definitely agree, but I would like to enable syntax like (create-node "x" 1 (create-node "y" 2))

nickt13:09:50

while also enabling syntax like (create-node "x" 1 (map (fn [child] (createNode...)) (range 10)) yknow what I mean?

nickt13:09:17

Maybe there's some macro sugar I could use to enable this while preserving a singular notion of create-node's interface

kennytilton16:09:37

I generally agree with others clucking their tongues over this, but for geenrating children it is definitely mad useful to be able to supply an atom or list or even nested list and then Just Flatten(tm) the input. The one constraint of course is being sure we can live with flatten ! In my fav use case I am building children for a UI/Dom node and I know flatten will stop when it gets to a node, and also I knw the parent demands a flat list of child nodes. Now I can bung together whatever I want creating children and have it all flattened before hitting the parent. hth

kennytilton16:09:34

I will check now on what clj flatten does. (I have my own from my CL days.)

kennytilton16:09:51

Ah, no, (flatten 42) => nil, we want 42.

kennytilton16:09:25

Here’s my wrapper for any thing from an atom to a nested list of atoms. The *par* bit can be ignored. The doall is likewise sth specific to my usage. Note that the rest arg guarantees some list will be seen by the innards to avoid the flatten discard of atoms. Oh, and we gracefully handle some nested generator deciding not to create any nodes, a classic in reactive/dynamic UIs, and discard nils.

(defmacro the-kids [& tree]
  `(binding [*par* ~'me]
      (assert *par*)
      (doall (remove nil? (flatten (list ~@tree))))))

Alex Miller (Clojure team)13:09:38

I've only ever regretted doing this

😄 3
➕ 12
borkdude13:09:27

@nickt In that case I would make create-node accept always a list of children. [(create-node ...)] is not much more typing than (create-node ...). or indeed create another fn

Alex Miller (Clojure team)13:09:38

that's probably what I would do as well (list)

nickt13:09:23

hmmm ok, yea, fair

borkdude13:09:49

btw I think props like {"x" 1} is also better than passing those separately

nickt13:09:55

why's that?

borkdude13:09:58

you could have something like INode where you represent multiple children as a NodeSeq which implements INode so passing multiple or one is valid in terms of your model about passing props: easier parsing, passing, etc

borkdude13:09:30

Take a look at hiccup, reagent, etc

nickt13:09:52

iiinteresting

nickt13:09:43

oh man, I should have just started here

nickt13:09:35

hmm ok, lot for me to think about. Thanks @borkdude and @alexmiller

jjttjj14:09:18

Is there a better way to use a different default printer than just starting a subrepl? for example if I want my repl to always use https://github.com/brandonbloom/fipp to print, I have the following evaluated when I start a repl:

...
(:require [clojure.main :as main]
          [fipp.edn :refer [pprint] :rename {pprint fipp}]
          [fipp.repl :refer [pst]])
...
(main/repl
    :caught pst
    :print fipp
    )
This works ok but then there are some times where it does cause subtle issues and it takes awhile to remember to try without fipp. Is this inevitable? I'm currently using inf-clojure on emacs.

practicalli-johnny18:09:45

CIDER uses fipp for all projects if you set it as the default (setq cider-pprint-fn 'fipp) Does the editor you are using support something similar?

vncz14:09:55

Does anybody know any good way to parse nested query strings in Pedestal? It is currently able to automatically turn qs in maps (`?q=1&b=2` => {:q 1 :b 2} ), but I would need something a little bit more elaborate, such as ?foo[bar]=baz => { :foo {:bar "baz" } }

vlaaad14:09:45

do you control the query params generated by client?

vncz14:09:27

Yes I do @vlaaad; I can reshape it in case there's a format that Pedestal will deserialise automagically

vlaaad14:09:49

IIRC query string is by definition is just a string

vlaaad14:09:09

e.g. k1=v1&k2=v2 is pure convention

vlaaad14:09:17

just dump your edn there 😄

vncz14:09:25

Ah, that ain't a bad idea after all

vncz14:09:40

Does Pedestal auto evaluates EDN strings to be a real map?

vlaaad14:09:45

that might be a dangerous idea 🙂

vncz14:09:56

Well yeah, if that calls functions and stuff

vncz14:09:08

(I would be assuming Pedestal would be doing some sanitisation?)

vncz14:09:33

I would regularly use a body, but this is a GET call and I want to go through a query string

vncz14:09:39

…but I do need to pass a deep object

vlaaad14:09:42

clojure.edn/read-string is safe

vncz14:09:56

…and I do have a spec for my object

vlaaad14:09:11

this is a dangerous idea if you’ll want to make this endpoint consumable by other clients as well

potetm14:09:25

not dangerous — perhaps annoying at best 😄

potetm14:09:35

it’s just an encoding

potetm14:09:24

idk, I guess I can see the argument for, “EDN doesn’t have an encoder in every lang.”

vlaaad14:09:46

that’s my argument, yes :)

vncz14:09:31

Well as long as there's a JSON to EDN converter I'd be ok I guess 🙂

vlaaad14:09:28

e.g. public

vncz14:09:40

My HTTP endpoint should receive a ::children from the Query String (somehow)

vncz15:09:06

Is there a way to declaratively set a spec check on a payload in Pedestal or shall I manually put this boilerplate every time? https://github.com/XVincentX/clojure-playground/blob/master/src/app/core.clj#L21-L23

Chris O’Donnell15:09:57

I would write an interceptor to do that

vncz15:09:01

Ok thanks @U0DUNNKT2 — I was thinking about the same, just wondering if Pedestal had something out of the box

Chris O’Donnell15:09:41

There is likely a library that provides such an interceptor, but I'm not sure how worthwhile it would be to bring in a dependency for that. Shouldn't be more than a few lines of code, and it's nice IMO to have full control over the behavior.

vncz16:09:42

Yes, makes sense. I'll see if I can come up with some code. Thanks!

vncz23:09:50

@U0DUNNKT2 Here it is. Likely not the best code ever, but it does the job.

Chris O’Donnell00:09:52

Looks good overall. A few small suggestions: * If you want to terminate request processing early, it's good practice to use terminate (http://pedestal.io/api/pedestal.interceptor/io.pedestal.interceptor.chain.html#var-terminate). * You might consider doing something like (assoc-in context [:request :parsed qstring] parsed-param) so that you're just adding data to your context map in the interceptor and not overwriting your request data. * Consider the option of separating the concerns of parsing the query params and validating them into separate interceptors.

Chris O’Donnell00:09:35

Also, there's a #pedestal channel that might be a good place to discuss further.

vncz00:09:52

All good point. I've read somewhere that Pedestal terminates if the response object is there, that's why I followed the convention

vncz00:09:26

@U0DUNNKT2 I do not understand the second point — how would assoc overwrite the request data?

Chris O’Donnell01:09:57

I'm not a pedestal expert and it's been a while since I've used the library. IIRC the request data is available in ctx under the :request key. If that is the case and you do (assoc ctx :request {:parsed parsed-param}), then you will dump whatever data was previously associated to :request. If that's not what's happening, then there is no problem. :)

vncz13:09:25

Oh yeah you're right, assoc is replacing

p4ulcristian16:09:40

Hello guys! I made a reservation system. The mechanism is: 1. read from mongo->decide if there is free space 2. if free, insert reservation, if not free, don't. This function is called add-reservation . My problem is, add-reservation is behind a server. I would need the to run only one add-reservation at a time, because there is a concurrency problem when multiple run at once. I use agent and send reservations to it. Is there a better way?

lukasz16:09:13

Locks are commonly used for this kind of stuff, but that's beyond Clojure's scope

Alex Miller (Clojure team)17:09:22

well, locks are fine if you need one. an agent is a good solution, assuming you have only one server

lukasz17:09:40

Definitely, locks only make sense if you have more than one server. Maybe I misunderstood the question :thinking_face:

hmaurer17:09:21

Hello! Quick question: what’s the term for an aggregate (e.g. a sum) that isn’t declaratively specified, but instead is updated imperatively on every event? e.g.

total_payroll = select sum(salary) from employee; 
v.s.
total_payroll = 0
on insert employee, total_payroll += salary
on delete employee, total_payroll -= salary
on update employee, total_payroll += (new_salary - previous_salary)
I know there is a term for it but I forgot what!

Jeff Evans17:09:53

running total? rolling sum?

p4ulcristian17:09:18

I'll check the locks too! thanks!

emccue20:09:24

@paul931224 Maybe not super helpful if its set in stone, but Mongo probably isn't the best database for this

emccue20:09:44

it sounds like you actually want strong consistency

emccue20:09:58

and your ultimate data size won't be that huge

emccue20:09:06

so...just use a sql database

emccue20:09:25

then you can do that whole process in a transaction

➕ 9
emccue20:09:30

you will run into more scaling problems trying to have what would effectively need to be a distributed lock on the reservations collection than with scaling a sql db

emccue20:09:03

if you don't need to scale ever and really can just have a single server for all time then server side locks would be just fine

emccue20:09:00

i know there is some degree of transaction support in mongo

emccue20:09:04

but truthfully I don't know that much about it

Noah Bogart21:09:41

namespace and protocol question: i have a protocol CostFns in game.core.abilities, and then (:require [game.core.abilities :refer [CostFns]]) it in game.core. I wrote a factory function to store the records that implement these in a var, so I can pass in a keyword to get the right record out, and if I print out the calls, the var is correctly filled during compilation. However, at a later point during compilation an error is thrown, saying that No implementation of method: :cost-name of protocol: #'game.core.abilities/CostFns found for class: game.core.Click

Noah Bogart21:09:04

has anyone run into something like this before?

Alex Miller (Clojure team)21:09:25

yes, you've reloaded the protocol at some point

Alex Miller (Clojure team)21:09:51

the protocol, when compiled, makes a Java interface and the records implement that interface

Alex Miller (Clojure team)21:09:04

if you reload the protocol, you make a new interface (same name), and the old records don't implement it

Noah Bogart21:09:15

oh interesting, okay. thank you

nickt21:09:50

hey all. Another quick question... I'd like to write a macro that takes N arguments such that if the first argument is a map literal, do nothing, otherwise inject an empty map at the front of the N arguments list. Seems straightforward enough, but how do I check the type of that first argument in a macro?

noisesmith21:09:43

that's only possible if the map is a literal, you can't check the type of an arg otherwise, it's just a symbol, you don't see the value the symbol resolves to locally until runtime

noisesmith21:09:22

of course the map being added to the arg list could be done at runtime, you just need to emit code that checks and modifies the arg list in that case

nickt21:09:40

hmm ok, yea, makes sense

noisesmith21:09:58

(defmacro foo-bar [& args] `(let [arg-list# (if (map? (first ~args)) ~args (cons {} ~args))] ...))

nickt21:09:57

cool, thanks!

nickt21:09:51

ok unrelated question: I'm aware of list? and vector?. Is there a similar predicate that is true if either list or vector?

Linus Ericsson06:09:39

(fn [o] (or (vector? o) (list? o)))

noisesmith21:09:27

perhaps you want sequential?

noisesmith21:09:49

that is also true for lazy-seqs and conses, which are not lists and are false for list?

noisesmith21:09:58

in fact, list? is so rarely useful I consider it a smell in code review

nickt21:09:05

ah, awesome

nickt22:09:52

oh, duh, map? won't work for me in that argslist check because all of the args will be maps heh. What's the idiomatic way to basically "type" a map? As in define a map as a type A or not

noisesmith22:09:08

you could have a special key or metadata, or use defrecord to have a map with a named type

noisesmith22:09:18

how does that first map have to be different from the rest?

nickt22:09:49

defrecord was the one! Thank you!