beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
piyer 2021-05-21T00:08:05.156700Z

yup, I wrote a macro with logging.

seancorfield 2021-05-21T00:32:58.156900Z

If you want to see what a variant of default options would be for logging:

(! 1574)-> clojure -M:rebel:test:dev/repl
Downloading: com/bhauman/rebel-readline/maven-metadata.xml from clojars
Selected port 61666 for the Socket REPL...
Starting Rebel Readline as the REPL...
[Rebel readline] Type :repl/help for online help info
dev=> (require '[next.jdbc :as jdbc] :reload-all)
nil
dev=> (def db-spec {:dbtype "h2:mem" :dbname "example"})
#'dev/db-spec
dev=> (def ds (jdbc/get-datasource db-spec))
#'dev/ds
dev=> (def lds (jdbc/with-logging ds println))
#'dev/lds
dev=> (jdbc/execute-one! lds ["create table foo (name varchar(32))"])
execute-one! [create table foo (name varchar(32))]
#:next.jdbc{:update-count 0}
dev=> (require '[next.jdbc.sql :as sql])
nil
dev=> (sql/insert! lds :foo {:name "Sean"})
execute-one! [INSERT INTO foo (name) VALUES (?) Sean]
nil
dev=> (sql/insert! lds :foo {:name "Piyer"})
execute-one! [INSERT INTO foo (name) VALUES (?) Piyer]
nil
dev=> (sql/find-by-keys lds :foo {:name "Piyer"})
execute! [SELECT * FROM foo WHERE name = ? Piyer]
[#:FOO{:NAME "Piyer"}]
dev=> 
https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/sql_logging.clj

seancorfield 2021-05-21T00:33:27.157200Z

That’s available on develop as an experiment, if you want to try it out via :git/url.

orpheus 2021-05-21T01:20:58.159800Z

Why can’t you get-in a list within a map? (get-in account [:parties 0 :id]) where account is

{:id "164684366"
 :origin "neuromancer"
 :balance 1000, 
 :parties (list {:firstName "party-fn-60390295" :lastName "party-ln-60390295" :id 99})}
It seems to work if :parties is a vector. Is there an easy get-in type function I use on lists within maps?

orpheus 2021-05-21T16:09:52.189400Z

Ooh thank you @seancorfield. I’m wondering, what do you two suggest as the best way to ramp up beginner clojure skills? Just read through the clojure docs consistently and learn the standard library as best I can and write a lot? It seems hard to know when I could be writing something better

seancorfield 2021-05-21T16:33:25.189800Z

@titanroark Practice, practice, practice. Do 4clojure problem, do the koans, do the http://exercism.io Clojure track. And read, read, read. There’s a lot of good material on http://clojure.org between all the guides and the reference sections and (especially) the whole REPL series there. And experiment with everything you read, using the REPL.

seancorfield 2021-05-21T16:34:54.190400Z

Although Clojure is “simple”, it takes a while to “grok” why it is the way it is (The Joy of Clojure book is good for learning about the “why” but it isn’t really a beginners’ book).

orpheus 2021-05-21T16:56:49.192500Z

Much appreciated, thank you.

raspasov 2021-05-23T02:24:58.249800Z

Watch all Rich Hickey talks.

raspasov 2021-05-21T01:28:48.160200Z

(get-in
 {:id      "164684366"
  :origin  "neuromancer"
  :balance 1000,
  :parties (vector {:firstName "party-fn-60390295" :lastName "party-ln-60390295" :id 99})}
 [:parties 0 :id])
;=> 99

raspasov 2021-05-21T01:30:23.160500Z

From a design perspective, the reason why it doesn’t work with list is because lists do not have an efficient lookup by index (that’s just my guess).

raspasov 2021-05-21T01:31:23.160700Z

As a general rule, I try to stick to vectors everywhere, unless lists are specifically needed (i.e. you need to append to the front efficiently, but that’s rare).

orpheus 2021-05-21T01:33:25.161100Z

gotchya, been meaning to look into when to use vectors vs lists

raspasov 2021-05-21T01:33:31.161300Z

;if you really need a list
(let [lookup-f (comp :id #(nth % 0) :parties)]
 (lookup-f
  {:id      "164684366"
   :origin  "neuromancer"
   :balance 1000,
   :parties (list {:firstName "party-fn-60390295" :lastName "party-ln-60390295" :id 99})}))
;=> 99

raspasov 2021-05-21T01:33:43.161500Z

^^ If you really need to use a list.

orpheus 2021-05-21T01:34:04.161700Z

I’ll probably change to vector, but I appreciate reading your code

✌️ 2
raspasov 2021-05-21T01:34:04.161800Z

But watch out, nth is an O(n) operation on a list.

raspasov 2021-05-21T01:34:57.162200Z

For short lists, likely more than fine.

orpheus 2021-05-21T01:35:55.162500Z

What’s considered short?

raspasov 2021-05-21T01:36:04.162700Z

Haha, good question 🙂

😄 3
raspasov 2021-05-21T01:36:38.163Z

Whatever doesn’t make people complain that things are taking too long 😝

raspasov 2021-05-21T01:37:02.163200Z

My gut feeling is that I would be comfortable up to 1000 items.

raspasov 2021-05-21T01:37:19.163400Z

And again, depends if that code is on a “hot path”.

raspasov 2021-05-21T01:37:34.163600Z

Are you calling it once an hour, or thousands of times per second?

orpheus 2021-05-21T01:39:36.164Z

For now, once in a while, so nothing bad. But I’ll keep that in mind using lists from now on, and the length of the list will never be more than like 5 so I’d be good either way probably

raspasov 2021-05-21T01:40:17.164200Z

It is considered a code smell, but I’m of the opinion that rules can be broken, if you know what you’re doing.

🙌 1
raspasov 2021-05-21T01:40:31.164400Z

For lists of 5, you should never run into a problem IMO.

✅ 1
raspasov 2021-05-21T01:44:16.164800Z

Btw one last improvement to that code:

;if you really need a list
(let [lookup-f (comp :id #(nth % 0 nil) :parties)]
 (lookup-f
  {:id      "164684366"
   :origin  "neuromancer"
   :balance 1000,
   :parties (list {:firstName "party-fn-60390295" :lastName "party-ln-60390295" :id 99})}))
;=> 99

🙌 1
raspasov 2021-05-21T01:44:57.165Z

(so it doesn’t throw an “Index out of bounds exception” if it can’t find the desired index) added a “not found” argument in the nth call

seancorfield 2021-05-21T01:47:45.165500Z

@titanroark This page has good background information on why you might want vector vs list etc https://clojure.org/reference/data_structures#Collections

👆 1
Jacob Rosenzweig 2021-05-21T05:00:24.169400Z

Do s/fdef specs get written out in the same file as the concrete function or is it usually kept in a test file in the test directory of a project?

jumar 2021-05-21T05:26:37.170400Z

I put them in the same file right above the function

👍 1
Jacob Rosenzweig 2021-05-21T05:30:02.170600Z

And because of the registry system in specs, the definition you create can be referred to with its associated atom? That's basically my understanding right now.

piyer 2021-05-21T05:44:56.170900Z

nice! does that log via log4j2 or simple stdout?

seancorfield 2021-05-21T05:54:33.171100Z

@munichlinux You supply the logging function(s) so it can do whatever you want.

seancorfield 2021-05-21T05:54:51.171300Z

(I’ve updated it a bit since the post above — see my note in #sql)

seancorfield 2021-05-21T05:58:09.171500Z

@rosenjcb Not sure what you mean by “atom” there — s/fdef specs are referenced by a fully-qualified symbol which is the name of the function they apply to (`s/def` specs are referenced by a fully-qualified keyword).

piyer 2021-05-21T06:05:24.171700Z

❤️

Jacob Rosenzweig 2021-05-21T06:06:00.171900Z

@seancorfield yup "atom" was the wrong term.

2021-05-21T13:38:37.173300Z

How do I add a ~/.lein/profiles.clj file?

2021-05-21T13:39:14.173600Z

you can literally just create the file

2021-05-21T13:39:43.174200Z

but most people consider it a bad idea (it introduces local failures that won't be reflected on other's setups)

2021-05-21T13:41:45.174700Z

I mostly just want to add stuff like https://github.com/venantius/ultra/ for better error messages

2021-05-21T13:42:34.175500Z

So I just create an empty profiles.clj file and add {:user {:plugins [[venantius/ultra "0.6.0"]]}} to it and it will work?

2021-05-21T13:42:48.175700Z

that's how it works yes

2021-05-21T13:44:28.177100Z

Awesome, thank you. I appreciate the feedback on local failures not being reflected on other’s setups, but at least for now I’m not working on a team and I still have a lot of learning to do before I do something like upload a library so I’m not super worried about that

NoahTheDuke 2021-05-21T13:46:01.178200Z

i think as long as you're only using libraries like ultra (that don't modify a given program's execution, only what's printed to your console), you should be fine

2021-05-21T13:46:52.179300Z

it comes up a lot when a beginner dips their feet into clojure, adds some weird tooling config in profiles.clj, goes away for a bit, then come back to learning clojure and show up here with weird errors, having forgotten they even set up profiles.clj

2021-05-21T13:47:41.179800Z

@nbtheduke I've seen really weird errors caused by the kind of "magic" di tricks those libs use

NoahTheDuke 2021-05-21T13:48:23.180100Z

oh word? that's annoying

2021-05-21T13:48:53.180600Z

I guess it's not super frequent, but it stands out in memory by how confusing it was

☝️ 1
NoahTheDuke 2021-05-21T13:49:23.180900Z

yeah, good at least to be aware

2021-05-21T13:56:13.181500Z

Interesting. I’ll make note of remembering what’s in my profiles.clj file.

2021-05-21T13:56:42.182500Z

or just remember to remove / move it if you hit weird errors

2021-05-21T13:56:58.182900Z

So I basically want to use Specter on just about every project. Since that modifies code, you would suggest not doing that in my profiles.clj and just manually putting it in my project.clj each time?

2021-05-21T13:57:42.183700Z

right, code you use for application logic does not belong in profiles.clj if you ever intend others to interact with that code in any way

2021-05-21T13:58:17.184400Z

and even if you are the only one using the code, it adds a weird failure point - change profiles.clj and now projects stop working

2021-05-21T13:59:35.185900Z

the best thing about a project manifest like lein's project.clj is that it works as a declarative standalone configuration which unambiguously points to versioned resources, using user level config tends to interfere with that

2021-05-21T14:44:00.186600Z

Okay, that makes sense to me. Thanks!

valerauko 2021-05-21T16:36:26.191400Z

can someone point me to resources about concurrency optimizations in java (and/or clj)? along the lines of writing a new executor to minimize time wasted on threads waiting on io and maximize throughput

2021-05-24T16:19:10.321300Z

With a modern OS, if you use the right IO API (the one java nio uses), time isn't wasted on threads waiting on IO - the IO call suspends the thread and the completion of the IO is the kernel's signal to wake the thread. the hard part from a developer point of view is managing the inter thread coordination (clojure's immutability helps make that a lot simpler, core.async can help too if used correctly)

2021-05-24T16:21:11.321500Z

the book Java Concurrency In Practice can help a lot if you are planning on micro-optimizing concurrent code https://jcip.net/ - it's a really counter intuitive domain and unless you are already experienced with concurrency, the optimizations you end up needing are likely not to look like the ones you are imagining right now.

2021-05-24T16:22:31.321800Z

for truly high throughput situations the dominating concerns end up being things like cache usage and playing nicely with speculative execution

valerauko 2021-05-25T06:40:36.361100Z

i'm using netty and the problem i'm facing is that the number of "worker" threads necessary varies wildly depending on the workload. if the final handler stalls a lot on io (for example calling out to jdbc or any other blocking network io), i'd want to use more threads to maximize cpu utilization, while for more cpu bound workloads i'd keep the thread count "minimal" to minimize thread competition and context switching costs. i was trying to figure out if there are any libraries providing an Executor that could do something like this for me, but no luck

2021-05-25T16:21:07.379700Z

sounds like you just want two thread pools with different configurations? the built in executors can handle this, and clojure itself offers the agent send pool (for CPU bound) and send-off-pool (as used by the future function) for IO bound

2021-05-25T16:22:13.379900Z

what's the bottleneck / performance pain point you are seeing? excessive time spent in context switches?

Franco Gasperino 2021-05-21T16:49:24.192400Z

any suggested reading on working with both java interop and native form error handling, errors as data, and best practices for composing while considering error handling?

phronmophobic 2021-05-21T19:10:55.193Z

maximizing throughput is usually highly workload dependent, so it's hard to offer suggestions without knowing what the use case is. A good starting point might be the Fork/join framework, https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html

Joni Hiltunen 2021-05-21T22:05:26.194200Z

does anyone know if there's a way to extract more info from that Parse object somehow? does it contain any? Instaparse and I tried looking at the docs but maybe I'm not so good at reading https://gist.github.com/Sose/9a68caa42bd0e25fe70e9d353b3503fe

Joni Hiltunen 2021-05-21T22:06:44.194500Z

the usage.txt file shows how I'm trying to use it

Joni Hiltunen 2021-05-21T22:11:01.195200Z

ahh apparently I can print it atleast.. but if I wanted to show something in my cljs web interface?

Joni Hiltunen 2021-05-21T22:12:45.195800Z

strjust gives me "[object Object]"

2021-05-21T22:17:24.195900Z

This is an interesting topic. First I think we need to decide what you mean by error. E.g 1. The system can't responsible continue. 2. There is a business case that's unaccounted for and we need to alert a human. Moving to a different topic, if a java lib is throwing unrecoverable execptions, then I assume we're discussing how to log that information. What else is there to do? Recovery? That seems rather specific to what's being done. Finally, I would assume composing error handling is similar to composing other functions. E.g a ring middle ware function might have a case where the db is down so it can't retrieve the file, at this point it's up to you too decided. What status code, etc..

Joni Hiltunen 2021-05-21T22:26:51.196600Z

also I need to figure out how to allow arg names in commands while inside a function def but not outside them :thinking-face:

Joni Hiltunen 2021-05-21T22:27:08.196900Z

or just let users introduce variables

Joni Hiltunen 2021-05-21T22:52:22.198100Z

What started as a fun project to practice Clojure & Clojurescript is turning into mainly a challenge in interpreting a custom programming language... I wish I had some kind of education in implementing languages 😄

sova-soars-the-sora 2021-05-21T23:18:20.199Z

@djonih sounds very intriguing... there is an #instaparse channel if you have specific questions and want to summon the masters... Do you have a clear idea of the desired result and what it looks like?

sova-soars-the-sora 2021-05-21T23:22:11.200100Z

Losing my marbles 😄

Joni Hiltunen 2021-05-21T23:22:19.200300Z

@sova I just want to know how to get more data, any data, from the Parse object that I get when the parse is invalid. I noticed I can (print it)in the repl to see an error message but how to get that message out of it otherwise?

sova-soars-the-sora 2021-05-21T23:23:06.200900Z

Great question. Let me do some snooping. In the intermediate time, are you familiar with the excellent online version of instaparse? http://instaparse-live.matt.is/

sova-soars-the-sora 2021-05-21T23:23:36.201300Z

That spits out errors automatically as you change the input. There is probably the source code for that somewhere

Joni Hiltunen 2021-05-21T23:25:04.201700Z

ah, yeah that's what I want basically..

Joni Hiltunen 2021-05-21T23:25:33.202200Z

https://github.com/mhuebert/instaparse-live I guess this is it? I think I'll be able to find it

sova-soars-the-sora 2021-05-21T23:25:56.202900Z

Yeah I did some digging, I think what you want is in app/compute.cljs with "insta/get-failure"

sova-soars-the-sora 2021-05-21T23:26:13.203200Z

anyway, it's around there somewhere ^.^ @djonih

sova-soars-the-sora 2021-05-21T23:26:22.203400Z

happy hacking

Joni Hiltunen 2021-05-21T23:26:53.203800Z

Ahh, (pr-str)is what I want I guess

sova-soars-the-sora 2021-05-21T23:26:58.203900Z

better than docs is working code to examine

Joni Hiltunen 2021-05-21T23:27:08.204200Z

thank you so much for helping!

1
sova-soars-the-sora 2021-05-21T23:27:15.204400Z

Oh yeah, there are some small caveats with clojurescript. pr-str for example.

sova-soars-the-sora 2021-05-21T23:27:25.204700Z

i don't know if there is a good list anywhere, that would be helpful.

sova-soars-the-sora 2021-05-21T23:27:30.204900Z

(of cljs caveats)

Franco Gasperino 2021-05-21T23:28:27.205100Z

In most all cases i've encountered, it would be #2 - i'm looking to capture either an unchecked java exception or a business case error for user reporting. I'm most interested in function composing. Do i pass error conditions around as a map with one or more keys, check for that key in peer functions, etc

Joni Hiltunen 2021-05-21T23:38:25.206Z

ah also found out about (cljs.pprint/pprint) ... can actually see the object and its keys and values that way

Joni Hiltunen 2021-05-21T23:40:02.206800Z

or I guess I should've tried (keys result) too but I completely forgot about that function and I didn't realize I could do that on a weird object thing