Fork me on GitHub
#beginners
<
2021-12-07
>
Cora (she/her)00:12:35

loop/recur is great when you need more fiddly behavior around looping, which AoC includes a bunch of. I used loop/recur 3 times in the first 5 days

👍 1
rfisher00:12:09

we have very similar solutions! I am happy to see you also wrote quite a lot of code for day 4. 🙂

Cora (she/her)00:12:04

it wasn't strictly necessary but it helped with clarity

seancorfield01:12:53

Don't forget we have a dedicated #adventofcode channel!

Cora (she/her)00:12:47

I haven't done day 6 yet

Cora (she/her)00:12:48

reduce is another fiddly one, you can do a lot of different kinds of looping and even exit early with reduced

Cora (she/her)00:12:24

I suck at transducers

Cora (she/her)00:12:48

I really ought to read that section in Clojure, The Essential Reference

rfisher00:12:14

Interesting. The thing that got me thinking was finding (map vector a b) was akin to Ruby's zip . The way people use those few basic functions (definitely including reduce) blows me away.

Cora (she/her)00:12:53

ahhh yeah, passing multiple collections to map does come in handy

rfisher00:12:27

Would NEVER have occurred to me.

Cora (she/her)00:12:52

err I guess that should be transposing

hiredman00:12:35

I'd sit down with clojure.core/for, it can map, filter, take-while, mapcat, and do cross products

👍 1
hiredman00:12:20

iterate+take-while can also take the place of a lot of loops

maziar rumiani00:12:14

Hello everyone, I'm new to Clojure and I'm struggling with setting up the environment on Windows and VS code. Is there a good tutorial for that?

Stuart01:12:12

I found it much easier to use Vs code and wsl. Installing on Linux was much less painful and the vscode remote wsl support is top notch.

👍 1
seancorfield01:12:41

@U02PLQ02QUD I'll second what Stuart said: if you can use WSL2 for your development, I would strongly recommend it. It's how I work. VS Code on Windows with the remote-wsl2 extension installed; everything Clojure-related on Linux (Ubuntu) on WSL2; VS Code makes it feel like everything is local on Windows -- and you avoid any and all weirdness around pathing/quoting and you'll find nearly all the tutorials and books assume macOS/Linux so it'll make your life a lot easier in that respect.

👍 1
chrisulloa00:12:04

Is it possible to create a future with the thread local of the parent thread that created the future?

hiredman01:12:33

generally speaking no, that isn't how thread locals work, futures run on another thread

chrisulloa01:12:23

Ah got it, thanks. I was hoping there’d be a nice workaround somewhere. Could just set the values again in the future body.

hiredman01:12:23

however dynamic bindings from the creating thread also exist when a future executes

🆒 1
hiredman01:12:08

the caveat there is they are only set!'able on the thread that did the dynamic binding

hiredman01:12:20

and that propagation is something clojure does specially for future and agent actions, if you create a thread by some other means the bindings won't get propagated, you can using something like bound-fn in that situation

chrisulloa01:12:48

got it thank you

chrisulloa01:12:14

that’s interesting actually, i wasn’t aware of what a future would capture

chrisulloa01:12:27

our company java library relies on thread local values for logging. so when we spawn futures from a thread the logger doesn’t function.

Steiner08:12:28

hey, how to translate this clojure code to java ??

(map map-fn list1 list2)

fearnoeval12:12:33

If you haven't found the answer yet, https://clojure.org/reference/java_interop#_calling_clojure_from_java should have most of the details you need. The caveat would be if list1 and list2 live in a Clojure namespace, in which case you'd need to cast the results of Clojure.var for them to a clojure.lang.Var so that you can call deref on them before passing them to map. Why? Because without the deref, you'd be doing the equivalent of (map #'map-fn #'list1 #'list2).

Lycheese12:12:37

Is there a way to catch an AssertionError produced by pre-post-conditions? E.g. the try block below doesn't catch the error:

(defn string-to-string [str]
  {:pre [(string? str)]
   :post [(string? %)]}
  str)

(comment
  (string-to-string "str")
  (string-to-string 10)
  (try
    (string-to-string 10)
    (catch Exception e
      (prn "caught" e)))
  *e)

flowthing12:12:34

(catch Throwable t ,,,)

Lycheese12:12:51

Ah, makes sense. Thank you very much 🙂

jumar13:12:16

Or catch only AssertionError

Aviv Kotek14:12:40

hi, new to clojure.spec, using it for data-validation, what would be an idiomatic way to transform spec action to client-error message? my logic is mostly: if (spec/valid? spec req-body): XYZ, else: return 422 "invalid req-body" but i'd like to improve the error msg to be more specific, 1. I have looked onto expound but prefer not to add another dependency, 2. I can generate logic on (s/explain-data) data structure result, so (2) seems like a good direction -- is this common? thx!

Alex Miller (Clojure team)14:12:12

that's the purpose of the explain-data

👍 1
sheluchin15:12:39

I have a collection (vector) that I would like to lazily transform into a map. How do? for produces a seq - no good; reduce isn't lazy; make my own with lazy-seq? I feel like I'm glossing over something here.

ghadi15:12:46

maps and vectors are never lazy, only seqs can be lazy

ghadi15:12:26

this is only lazy in the values -- it delays their computation, but the keyset is fixed

ghadi15:12:38

(from a 15 second reading)

ghadi15:12:59

i stand by my statement

🙂 1
ghadi15:12:34

(into {} (for [x coll] ...)) or (into {} (map a-function-that-produces-entries coll))

ghadi15:12:45

are two ways to turn a seq into a map

sheluchin15:12:46

Is the seq of a map (`[[k v]]`) what you call an "entry"?

ghadi15:12:54

yup, great question

ghadi15:12:58

[k v] <-- entry

ghadi15:12:13

a vector of two elements is an entry

sheluchin15:12:32

Hmm, so if I want to delay evaluation, produce a seq of entries instead of a map and then into {} when I go to realize it?

sheluchin15:12:05

I think wrapping the map-generation in a lazy-seq would also have the desired effect of delaying map computation, no?

dpsutton15:12:16

do you want to lazily populate a map with values when they are looked up? It sounds like you might want a cache

dpsutton15:12:36

but it seems like there are some things that are at cross purposes. Having a sequence involved means you have to realize a bunch of entries in order to find the entry you want. Which doesn’t seem great. If you can manage to go from a seq based computation to more direct computation, perhaps a cache or a memoized function would provide what you are wanting?

sheluchin15:12:01

No, I have a data source that lazily gives me some starting values. I want to augment those values with some of my own computations but delay the computations until my consumer is ready for the chunk.

dpsutton15:12:51

ah. then i was incorrect 🙂

sheluchin15:12:48

It's okay, it's still helpful discussion that I appreciate 🙂

indy15:12:15

Are you delaying the computation of the value or the key or both?

sheluchin15:12:31

Value. I know what the keys are.

indy16:12:54

Asked coz there could be key collisions if they key was also going to be computed lazily. I think the library you linked is quite interesting.

sheluchin16:12:56

It's a set of accumulations into predictable keys, so no risk of collision.

John Bradens20:12:29

Has anyone ever done tutoring for clojure? Is that a thing? Has anyone tried being tutored and found it to be helpful? Do you know where I'd find one? I tried looking at online tutor websites but clojure is too niche for those I think

emccue20:12:06

i tutor people online pretty much constantly over discord

emccue20:12:15

i can just add you to the mix if you want

emccue20:12:25

emccue#1975

John Bradens20:12:51

Cool I will think about it thanks

John Bradens20:12:00

I've never used discord before so I'll have to try it out

John Bradens20:12:18

Do I just go to http://discord.com and search for emccue#1975?

emccue22:12:27

yeah its an app

naxels21:12:27

Can anybody tell me how to change this to only run (filter …) (if video-title-filter)?

(->> playlist-items
                                                (filter (fn [playlist-item] (str/includes? (str/lower-case (get-in playlist-item [:snippet :title] "")) video-title-filter)))
                                                (transform-playlist-items))

naxels21:12:52

Looks like I might have to use (cond->>) but can’t quite figure out how it would fit this example

naxels21:12:54

I think this does the trick:

(cond->> playlist-items
                                                (some? video-title-filter) (filter (fn [playlist-item] (str/includes? (str/lower-case (get-in playlist-item [:snippet :title] "")) video-title-filter)))
                                                true (transform-playlist-items))

ghadi21:12:06

try to avoid some? when you're just looking for truthiness

jumar08:12:55

I see some people prefer some? for things that aren't booleans to better communicate that this thing is either nil or not-nil (and false doesn't make sense for the thing). Any thoughts on that?

naxels21:12:31

what should I use?

naxels21:12:41

it’s either nil or value

naxels21:12:46

when not nil, filter

ghadi21:12:52

(cond->> coll
  video-title-filter (filter .......................))

naxels21:12:06

hahaha so simple, i should have thought of that

ghadi21:12:07

some? returns true when you pass it false

ghadi21:12:31

it does not mean the thing people think it means from other programming languages

naxels21:12:44

on the final line, I pass true only because it should always do this step

naxels21:12:48

is that actually needed?

ghadi21:12:27

if you want that unconditionally applied and are using cond->>, then yeah you need the true

ghadi21:12:05

you can also do: (transform-playlist (cond->> coll video-filter (filter ....))

ghadi21:12:43

or even (transform (if video-filter (filter ... coll) coll))

naxels21:12:47

thread the first part, always do the 2nd part

ghadi21:12:02

lots of ways

naxels21:12:39

if you break it down, due to only 2 or 3 steps, I don’t need the threading piece

naxels21:12:14

i’m looking to abstract the filter function as well, however filter pred takes 1 arg

naxels21:12:18

(filter (fn [playlist-item] (str/includes? (str/lower-case (get-in playlist-item [:snippet :title] "")) video-title-filter)))

naxels21:12:36

while video-title-filter should also be passed to the abstracted fn

naxels21:12:49

i am thinking of creating a closure fn for it

naxels21:12:52

any suggestion?

naxels22:12:25

@ghadi, thanks for your help!

naxels22:12:47

i’m going to leave the filter fn for now in order to make a release and get some sleep ha

Alexis Schad22:12:18

I'm not directly answering your question, but I like not to mix "complex" stuff with simple ones, espacially when using the -> macro. Something like:

(defn filter-playlist-items [title-filter playlist-items]
  ...)

(->> playlist-items
    (filter-playlist-items video-title-filter)
    (transform-playlist-items))
is more readable, for me, and it's easier to add if in the function without converting the whole ->> into a cond->> . Also you can unit test the filter-playlist-items function independently.

naxels22:12:43

Thanks! You make a good point about the testability of the new filter-playlist-items fn

naxels22:12:37

I usually try to make filter work without abstracting filter away from the place it's used

naxels22:12:59

Putting filter-... in the fn name also makes it obvious what's happening

dpsutton22:12:33

I find the easiest way to test these kinds of things is to ensure that you have a single pure predicate and then wire it up with (filter pred ...). Testing filter-playlist-items tends to be a bit more annoying that testing a predicate returned from (title-filter args)

Alexis Schad23:12:21

True! But here he wanted to add some extra code around the filter (make it conditionnal to some variable). It is still possible to create another function just for the inner filter predicate.

dpsutton23:12:29

my suggestion in the next comment is to put the conditional inside of the filter function and then unconditionally always use that filter pred

Alexis Schad23:12:00

I read it afterwards yes, it's elegant even if a bit suboptimal

dpsutton23:12:04

Also, another way to get around the cond-> is if your title-filter is the function that does the no-op. Ie, always create and filter using the title filter, but if there is no snippet title to filter on, have it return true on all movies (transform (filter (title-filter args) items))

👍 1