Fork me on GitHub
#beginners
<
2021-05-21
>
orpheus01:05:58

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?

raspasov01:05:48

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

raspasov01:05:23

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

raspasov01:05:23

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

orpheus01:05:25

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

raspasov01:05:31

;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

raspasov01:05:43

^^ If you really need to use a list.

orpheus01:05:04

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

✌️ 5
raspasov01:05:04

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

raspasov01:05:57

For short lists, likely more than fine.

orpheus01:05:55

What’s considered short?

raspasov01:05:04

Haha, good question 🙂

😄 8
raspasov01:05:38

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

raspasov01:05:02

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

raspasov01:05:19

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

raspasov01:05:34

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

orpheus01:05:36

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

raspasov01:05:17

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

🙌 3
raspasov01:05:31

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

3
raspasov01:05:16

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

🙌 2
raspasov01:05:57

(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

seancorfield01:05:45

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

👆 3
orpheus16:05:52

Ooh thank you @U04V70XH6. 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

seancorfield16:05:25

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

seancorfield16:05:54

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

orpheus16:05:49

Much appreciated, thank you.

raspasov02:05:58

Watch all Rich Hickey talks.

Jacob Rosenzweig05:05:24

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?

jumar05:05:37

I put them in the same file right above the function

👍 3
Jacob Rosenzweig05:05:02

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.

seancorfield05:05:09

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

Jacob Rosenzweig06:05:00

@U04V70XH6 yup "atom" was the wrong term.

Rob Haisfield13:05:37

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

noisesmith13:05:14

you can literally just create the file

noisesmith13:05:43

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

Rob Haisfield13:05:45

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

Rob Haisfield13:05:34

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

noisesmith13:05:48

that's how it works yes

Rob Haisfield13:05:28

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

NoahTheDuke13:05:01

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

noisesmith13:05:52

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

noisesmith13:05:41

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

NoahTheDuke13:05:23

oh word? that's annoying

noisesmith13:05:53

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

☝️ 2
NoahTheDuke13:05:23

yeah, good at least to be aware

Rob Haisfield13:05:13

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

noisesmith13:05:42

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

Rob Haisfield13:05:58

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?

noisesmith13:05:42

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

noisesmith13:05:17

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

noisesmith13:05:35

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

Rob Haisfield14:05:00

Okay, that makes sense to me. Thanks!

valerauko16:05:26

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

phronmophobic19:05:55

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

noisesmith16:05:10

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)

noisesmith16:05:11

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.

noisesmith16:05:31

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

valerauko06:05:36

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

noisesmith16:05:07

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

noisesmith16:05:13

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

Franco Gasperino16:05:24

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?

drewverlee22:05:24

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

Franco Gasperino23:05:27

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

drewverlee00:05:50

Hash maps are great for carrying around named information, so that seems reasonable to me.

Joni Hiltunen22:05:26

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 Hiltunen22:05:44

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

Joni Hiltunen22:05:01

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

Joni Hiltunen22:05:45

strjust gives me "[object Object]"

Joni Hiltunen22:05:51

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 Hiltunen22:05:08

or just let users introduce variables

Joni Hiltunen22:05:22

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-sora23:05:20

@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-sora23:05:11

Losing my marbles 😄

Joni Hiltunen23:05:19

@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-sora23:05:06

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-sora23:05:36

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

Joni Hiltunen23:05:04

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

Joni Hiltunen23:05:33

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

sova-soars-the-sora23:05:56

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

sova-soars-the-sora23:05:13

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

Joni Hiltunen23:05:53

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

sova-soars-the-sora23:05:58

better than docs is working code to examine

Joni Hiltunen23:05:08

thank you so much for helping!

2
sova-soars-the-sora23:05:15

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

sova-soars-the-sora23:05:25

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

Joni Hiltunen23:05:25

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

Joni Hiltunen23:05:02

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