Fork me on GitHub
#beginners
<
2019-04-29
>
ben12:04:05

If I want to execute 2 statements in each branch of an if statement, I can wrap them in do:

(if x
  (do (a!) (b x))
  (do (c!) (d x))
To me this seems a bit ugly. Is there a preferred way to do this? I could wrap each branch in it’s own function?

Ivan Koz12:04:54

its not ugly, just format like that, so each expression on its expected place

(if x
  (do (a!) 
      (b x))
  (do (c!)
      (d x)))

manutter5112:04:32

Wrapping each in a function could be nice if you give each function a descriptive name. That way your if can become almost documentation in itself.

Ivan Koz12:04:52

that's also true

Ivan Koz12:04:17

but, a b c d names maybe descriptive enough already

ben12:04:29

Cool, okay thanks

dharrigan12:04:33

Say I have a map and I'm destructuring like so: (let [{prefix :prefix suffix :suffix} config] (do (println (or suffix ".html")) (println (or prefix "foo/")))), I was wondering if there was a more idomatic way of "defaulting" values for keys that aren't set. I see that destructuring, you can use ":or", but that seems to work only once.

dharrigan12:04:31

(i.e., (let [{prefix :prefix suffix :suffix :or {suffix ".html"} config]).....)

dpsutton12:04:48

you can merge defaults in. (let [{:keys [prefix suffix]} (merge inputs defaults)] ... )

dharrigan12:04:30

so, I setup defaults into a map first, then merge it in

dharrigan12:04:35

nice! works beautifully

dpsutton12:04:32

of course happy coding 🙂

orestis13:04:53

:or should work for more than one key. Also, I think you should merge the input into the defaults.

dharrigan13:04:13

I think or can only be used once, here's an example of it failing...

dharrigan13:04:23

(let [{prefix :prefix :or {prefix "foo"} suffix :suffix :or {suffix "bar"}} config]....

dharrigan13:04:35

repl: Duplicate key: :or

dharrigan13:04:47

Perhaps I got the syntax wrong, however 🙂

orestis13:04:00

Oh yeah, just put all the keys inside :or, in a single map

dharrigan13:04:27

Thanks 🙂 I do like the merge one better 'tho, feels cleaner 🙂

dharrigan13:04:41

But thanks for info on :or anyhoo 🙂

dharrigan13:04:44

Good to learn.

orestis13:04:31

Yeah the effect is the same. Not sure which one is more idiomatic.

dharrigan13:04:18

I think the merge would end up being cleaner, as although my example only has two keys, I actually need a config map of about 10 or so options, with defaults for each

dharrigan13:04:33

having a big [let with lots of :or's could be wearisome on the eye 🙂

orestis13:04:02

Single :or :)

dharrigan13:04:21

sorry, you're right.

orestis13:04:24

But yeah for 10 keys you might want to def it out in its own var anyway

dharrigan13:04:51

feels nicer.

orestis13:04:16

Easier to reuse, spec etc.

dharrigan13:04:36

thank you! 🙂

bronsa13:04:02

note that merge and :or behave slightly differently

dpsutton13:04:57

when the value is present but nil? {:value nil}?

bronsa13:04:35

:or works via contains?

bronsa13:04:56

if you (merge defaults inputs) the effect should be the same

bronsa13:04:06

but not the other way round like you wrote

CyberSapiens9718:04:47

how to use threading macro ->> with something like dissoc ?

seancorfield18:04:57

@cybersapiens97 What are you trying to do? Pass in a list of keys to be dissoc'd?

CyberSapiens9718:04:37

(->> stats
                        (filter #(= (:isalbum %) 1))
                        (dissoc :id :userid :isalbum)
                        (clojure.set/rename-keys {:medianame :title}))

chrisulloa18:04:25

dissoc needs hashmap as first argument

chrisulloa18:04:33

so does clojure.set/rename-keys

seancorfield18:04:57

@cybersapiens97 are you sure that you want the updated stats as the last argument of rename-keys? I think you want it as the first arg?

seancorfield18:04:53

(-> stats
    (->> (filter #(= (:isalbum %) 1)))
    (dissoc :id :userid :isalbum)
    (clojure.set/rename-keys {:medianame :title}))
seems more likely correct

seancorfield18:04:29

So I would probably do this:

(-> (filter #(= (:isalbum %) 1) stats)
    (dissoc :id :userid :isalbum)
    (clojure.set/rename-keys {:medianame :title}))

dpsutton18:04:38

filter won't return something dissoc can work with

CyberSapiens9718:04:12

that's the problem, i think i can't use threadring macro on this situation

seancorfield18:04:34

You want to map those things over the list. Use a transducer 🙂

dpsutton18:04:36

(->> filter map) should do it

CyberSapiens9718:04:22

well, didn't thought about map

CyberSapiens9718:04:29

on this situation i think it's better

dpsutton18:04:26

good practice to write it with the thread macro and then make a transducer version right after

seancorfield18:04:08

(->> stats
     (filter ...)
     (map #(-> % (dissoc :id :userid :isalbum) (set/rename-keys {:medianame :title}))))
how about that?

👍 4
seancorfield18:04:16

I'd probably write a function for the data cleanup and then it would be (->> stats (filter ...) (map cleanup-media))

CyberSapiens9718:04:04

yeah i'll do it later because right now i'm finishing some handler functions from Ring

CyberSapiens9718:04:21

but i realized that it wasn't a good idea handling data transformation directly in handlers

CyberSapiens9718:04:04

i'll hand this to the specific components and pass directly the http requests that i receive to them. so i can receive the data in the correct shape directly on the handlers

CyberSapiens9718:04:23

otherwise my handlers end up being a complete mess

CyberSapiens9718:04:47

very sad because i'll have to refactor a lot of code, but this was a great insight

athomasoriginal19:04:10

Doing some cljs interop and I found myself writing this:

(->> (.. js/document (createElement "div"))
     (#(.. js/document -body (appendChild %))))
It always feels ugly to do (#(.. js/document -body (appendChild %)). Is there a cleaner way to do this that I am just not seeing?

noisesmith20:04:57

(->> (.. js/document ...) appendChild (.. js/document -body)) - ->> isn't semantic, it's syntactic, so it doesn't matter that appendChild has no meaning outside the .. call

noisesmith20:04:22

the compiler sees the right thing in the output

noisesmith20:04:44

also I think (->> js/document (.-body) (.appendChild (.createElement js/document "div")) is equivalent, I don't have a cljs to test it on right now though

athomasoriginal20:04:50

Thanks! I will give those a try 🙂

okwori20:04:35

For a vector say [0 0 0 1 0 1 0 0 0 0 1 0 0], I would like to return a vector with occurrences of >=3 0s trimmed to 00 Played around with frequencies, subvec, remove, take-while. filterv, still not cutting it...

Ivan Koz21:04:00

@simon i don't understand what you are trying to say, provide sample input->output data

okwori21:04:27

@nxtk input: [0 0 0 1 0 1 0 0 0 0 1 0 0] output: [0 0 1 0 1 0 0 1 0 0]

Ivan Koz21:04:55

so any seq of zeroes over 2 trimmed down to 2

Raymond Ko21:04:34

YOLO first attempt.

Ivan Koz21:04:38

above as transducer + minor optimizations

(def v1 [0 0 0 1 0 1 0 0 0 0 1 0 0])

(def x-trim (comp (partition-by identity)
                  (mapcat (fn [s]
                            (if (and (zero? (first s))
                                     (>= (count s) 3))
                              [0 0]
                              s)))))
  
(transduce x-trim conj v1)

👍 16
Vincent Cantin21:04:05

remove the flatten, use mapcat instead of map.

Ivan Koz21:04:16

right, thanks

Vincent Cantin21:04:49

(into [] (x-trim) v1) might work faster, it uses a transient data-structure when appending.

Ivan Koz21:04:34

@vincent.cantin tested with criterium, for 1,10,1000k elements barely any difference

(map #(criterium.core/quick-bench (into [] (x-trim) %)) [ve3 ve4 ve6])
Execution time mean : 198.320130 µs
Execution time mean : 2.201093 ms
Execution time mean : 218.993782 ms
 
(map #(criterium.core/quick-bench (transduce (x-trim) conj %)) [ve3 ve4 ve6])
Execution time mean : 256.688980 µs
Execution time mean : 2.587495 ms
Execution time mean : 259.473071 ms

👍 16
Vincent Cantin21:04:55

Also, x-trim does not have to be a function. A simple def is fine too.

Ivan Koz21:04:35

there is no difference because we create a new trie so transients do nothing, they are useful for updates not sure how into can utilize that feature

Ivan Koz21:04:53

oh i remember, transient collection uses extended tail node, so there is minor boost

Nezteb23:04:24

What is the go-to method of adding gradual typing to Clojure? I assume there aren’t any tools to add actual static types? Just type hints and such? Similar to Python and Elixir?

Ivan Koz23:04:22

@nbetzen spec also somewhat replaces typing

Ivan Koz23:04:23

watch this talk, maybe it's gonna give you some ideas

drone23:04:55

note that there’s now a spec2 under active development. lots of things are still in flux. and spec is definitely not intended to replace types

drone00:04:47

spec is designed as a testing tool. spec somewhat replaces types if you believe generative testing can replace a type system

drone00:04:05

which is a perfectly reasonable opinion to hold

Ivan Koz00:04:13

yeah i'm not arguing, i just think type systems and data verification\\contracts overlap in their nature

Michael Stokley00:04:33

I'm not sure i'd agree that spec replaces (static) typing

Michael Stokley00:04:37

it's runtime, right?

Ivan Koz00:04:48

replaces is a bad word i agree

Michael Stokley00:04:47

hope that didn't come across as a pile on. 🙂 i agree there's overlap there

Ivan Koz00:04:29

np, words are hard to master, always had a problem of choosing right words, my mind is more objective\abstract

seancorfield01:04:53

@UCF779XFS I think considering spec as a "testing tool" is overly narrow. Our biggest use of spec at work is definitely in production code rather than testing code.

drone01:04:25

I agree that spec doesn’t need to be only a “testing tool”, but the design has favored that use case (e.g., requiring generators for fn arguments)

drone01:04:57

and it feels like I’m working against the language/library when using specs for runtime “type” checking

drone01:04:18

to be fair, it’s under development. but the direction of spec2 doesn’t seem to be different

seancorfield03:04:50

We're using spec very heavily in production -- and have been for a long time -- and we have a branch running on spec2, ready to go as soon as Alex signals it is "stable".

seancorfield03:04:06

We're looking at schema/`select` as a way to consolidate several current layers of specs -- they should share a common schema and then have specific select variants in different parts of the code.