yup, I wrote a macro with logging.
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.cljThat’s available on develop as an experiment, if you want to try it out via :git/url
.
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?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
@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.
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).
Much appreciated, thank you.
Watch all Rich Hickey talks.
(get-in
{:id "164684366"
:origin "neuromancer"
:balance 1000,
:parties (vector {:firstName "party-fn-60390295" :lastName "party-ln-60390295" :id 99})}
[:parties 0 :id])
;=> 99From 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).
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).
gotchya, been meaning to look into when to use vectors vs lists
;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
^^ If you really need to use a list.
I’ll probably change to vector, but I appreciate reading your code
But watch out, nth
is an O(n) operation on a list.
For short lists, likely more than fine.
What’s considered short?
Haha, good question 🙂
Whatever doesn’t make people complain that things are taking too long 😝
My gut feeling is that I would be comfortable up to 1000 items.
And again, depends if that code is on a “hot path”.
Are you calling it once an hour, or thousands of times per second?
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
It is considered a code smell, but I’m of the opinion that rules can be broken, if you know what you’re doing.
For lists of 5, you should never run into a problem IMO.
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
(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
@titanroark This page has good background information on why you might want vector vs list etc https://clojure.org/reference/data_structures#Collections
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?
I put them in the same file right above the function
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.
nice! does that log via log4j2 or simple stdout?
@munichlinux You supply the logging function(s) so it can do whatever you want.
(I’ve updated it a bit since the post above — see my note in #sql)
@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).
❤️
@seancorfield yup "atom" was the wrong term.
How do I add a ~/.lein/profiles.clj file?
you can literally just create the file
but most people consider it a bad idea (it introduces local failures that won't be reflected on other's setups)
I mostly just want to add stuff like https://github.com/venantius/ultra/ for better error messages
So I just create an empty profiles.clj file and add {:user {:plugins [[venantius/ultra "0.6.0"]]}}
to it and it will work?
that's how it works yes
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
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
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
@nbtheduke I've seen really weird errors caused by the kind of "magic" di tricks those libs use
oh word? that's annoying
I guess it's not super frequent, but it stands out in memory by how confusing it was
yeah, good at least to be aware
Interesting. I’ll make note of remembering what’s in my profiles.clj file.
or just remember to remove / move it if you hit weird errors
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?
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
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
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
Okay, that makes sense to me. Thanks!
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
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)
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.
for truly high throughput situations the dominating concerns end up being things like cache usage and playing nicely with speculative execution
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
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
what's the bottleneck / performance pain point you are seeing? excessive time spent in context switches?
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?
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
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
the usage.txt file shows how I'm trying to use it
ahh apparently I can print
it atleast.. but if I wanted to show something in my cljs web interface?
str
just gives me "[object Object]"
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..
also I need to figure out how to allow arg names in commands while inside a function def but not outside them :thinking-face:
or just let users introduce variables
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 😄
@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?
Losing my marbles 😄
@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?
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/
That spits out errors automatically as you change the input. There is probably the source code for that somewhere
ah, yeah that's what I want basically..
@djonih https://github.com/mhuebert/instaparse-live/blob/master/src/app/compute.cljs#L52
https://github.com/mhuebert/instaparse-live I guess this is it? I think I'll be able to find it
Yeah I did some digging, I think what you want is in app/compute.cljs with "insta/get-failure"
anyway, it's around there somewhere ^.^ @djonih
happy hacking
Ahh, (pr-str)
is what I want I guess
better than docs is working code to examine
thank you so much for helping!
Oh yeah, there are some small caveats with clojurescript. pr-str for example.
i don't know if there is a good list anywhere, that would be helpful.
(of cljs caveats)
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
ah also found out about (cljs.pprint/pprint)
... can actually see the object and its keys and values that way
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