Fork me on GitHub
#beginners
<
2019-02-19
>
clojure beginner06:02:50

Just trying to write a function that returns distinct elements from a vector . Is it ok to use the 'initial value' in reduce like this ? (defn accumulate-dups [result item] (let [ map-unq (first result) vec-unq (second result)] (if-not (contains? map-unq item) [(assoc map-unq item 0) (conj vec-unq item)] result))) (second (reduce accumulate-dups [{} []] [1 10 9 9 2 4 4 7 8] ))

Jan K10:02:01

This is an easier way: (into [] (distinct) [1 10 9 9 2 4 4 7 8])

Jan K10:02:55

Or if you don't need the result to be a vector, just (distinct [1 10 9 9 2 4 4 7 8])

manutter5112:02:34

@UEHMNPFC5 Your code looks workable, but I can see a couple improvements you could make. First of all, you can use destructuring to simplify your handling of the results, like this:

(defn accumulate-dups [result item]
  (let [[map-unq vec-unq] result]
    ...

manutter5112:02:28

This will automatically separate out the items that are inside the result for you without having to write out first and second.

manutter5112:02:03

Next, you are using a map to check for values you’ve already seen, which is how I would do it if I were using a language that didn’t have sets. Clojure, however, does have sets, so that’s what I’d use here:

(defn accumulate-dups [result item]
  (let [[set-unq vec-unq] result] ;; `set-unq` will be a set instead of a map
    (if-not (contains? set-unq item) 
      [(conj set-unq item) (conj vec-unq item)] 
      result)))

manutter5112:02:11

and you’d call it like (second (reduce accumulate-dups [#{} []] [1 10 9 9 2 4 4 7 8]))

manutter5112:02:35

(notice #{} instead of {} in your initializer)

manutter5112:02:14

The cool thing about sets is that you can use them as functions, to test if an item is a member of a set, so you can do this:

(defn accumulate-dups [result item]
  (let [[set-unq vec-unq] result]
    (if-not (set-unq item) ;; set-unc is a set, so it automatically tests for `contains?`
      [(conj set-unq item) (conj vec-unq item)]
      result)))

manutter5112:02:37

It’s also very easy to turn a vector into a set on the fly, so you don’t need to carry the map-unq value around inside your result. Instead you can just write this:

(defn accumulate-dups [result item]
  (let [seen? (set result)]
    (if-not (seen? item)
      (conj result item)
      result)))

manutter5112:02:59

and call it like this: (reduce accumulate-dups [] [1 10 9 9 2 4 4 7 8])

jaihindhreddy-duplicate13:02:56

A set when called with an arg returns the arg if the set contains it, otherwise returns nil. So it acts as a predicate that checks membership only for truthy members. (map #(if (#{1 nil false} %) "yes" "no") [2 1 nil false]) Bit of a gotcha here.

manutter5113:02:30

Yeah, I forgot to mention that, thanks for pointing that out (especially since this is for #beginners)

clojure beginner16:02:06

thanks so much! @manutter51 for walking me through this. I particularly like the way the set is constructed on the fly. So do you decide on whether carrying the map around / create a new set on every invocation of accumulate-deps depending upon the size of the input vector or you just write the crisp code straight away . thanks @UBU6QCSJH for pointing out the gotcha.

manutter5117:02:50

I would usually use (set result) every time around, unless/until the performance started to suffer. It’s fairly easy to switch back to passing the seen? set back in as an argument if you need to later on, so I’d start with the simpler version by default.

jaihindhreddy-duplicate17:02:23

In reality though, a lot of these things have high quality implementations in clojure.core, so you don't need to write them yourself (like distinct). Clojure gives you a far larger vocabulary than the typical map, filter and reduce most languages that claim to support functional programming give you.

💯 5
clojure beginner06:02:49

the order of elements in the vector need to be maintained in the result

chocksmith12:02:53

I do this to get time in millisecond. :date value is an object of java.util.Date (.getTime (:date myobj)) What is the idiomatic way of getting zero if the date obj is null?

Jan K12:02:05

You can do (or (some-> (:date myobj) .getTime) 0)

chocksmith12:02:58

ohh! Beautiful. tks!

Tessy Thomas14:02:27

I have to access the inner cropper object from the ready event object. I am using cropperjs. The event object is as in the below image. But when I try to get (.-target event), I am getting HTML tag as <img src="https://static.pexels.com/photos/248797/pexels-photo-248797.jpeg" class="spots_ui_components_cropper--cropped-image27970 spots_ui_components_cropper--cropped-image cropper-hidden">

joelsanchez14:02:30

as shown in the readme, you should use (js-this) inside your ready fn to get it

joelsanchez14:02:33

new Cropper(image, {
  ready() {
    // this.cropper[method](argument1, , argument2, ..., argumentN);
    this.cropper.move(1, -1);

    // Allows chain composition
    this.cropper.move(1, -1).rotate(45).scale(1, -1);
  },
});

Tessy Thomas05:02:28

I tried js-this, this-as etc. But, they all are returning the HTML tag.

joelsanchez14:02:20

(js/Cropper. image
          #js{:ready (fn []
                       (let [cropper (.-cropper (js-this))]
                         (etcetera)))})

Mario C.21:02:10

what are some commons reasons for a try-catch's catch block not being triggered and propagating up to the calling function's catch function?

Alex Miller (Clojure team)21:02:11

for me, it's usually that my code has a bug :)

Alex Miller (Clojure team)21:02:49

best to validate exception actually being thrown and with the type you expect

Alex Miller (Clojure team)21:02:11

(I'm assuming clj, but there may be gotchas I'm unfamiliar with in cljs)

Mario C.21:02:57

Okay so a try-catch's catch function will always fire. Is there a catch-all exception I can use? I thought (catch Exception e . . . ) was the catch all

Mario C.21:02:16

but perhaps it is and like you mentioned my code has a logic error

Alex Miller (Clojure team)21:02:25

Throwable is the top of the hierarchy

Mario C.21:02:02

What about exception handling within futures? Does this change anything?

Mario C.21:02:55

(def list-names '("Tom" "Harry" "Richard"))

(defn throw-func
  [nme]
  (str "hello " nme)
  (throw (Exception. "I just threw you an error")))

(defn tc-test
  []
  (let [futures (map (fn [name]
                       (let [sample (fn [name]
                                      (try
                                        (println "Sanity Check")
                                        (throw-func name)
                                        (catch Exception e
                                          (println "Got an error here!"))))]
                         (future (sample name))))
                     list-names)
        response (doall (map deref futures))]
    1))

(defn foo
  []
  (try
    (tc-test)
    (catch Exception e
      (println "Handling error here as well"))))
I have this bit of code... Not exactly this but the structure is close enough. In my repl this works as expected and I am seeing "Got an error here!" but when running the app version of the example I see "Handling error here as well".

Alex Miller (Clojure team)21:02:29

futures are in a different thread with a different stack so you'll catch it at Got an error, and never in the one in foo

futuro21:02:02

(Another thing that’s tripped me up — though doesn’t seem to be tripping you up — is thinking I was going to catch an Exception but an Error was thrown, or vice versa. In that case I often used Throwable because the distinction didn’t matter for my use case.)

noisesmith21:02:04

deref on a future with an uncaught exception will rethrow at the point of deref, but there's no path in that code for that which I can see

hiredman21:02:13

the code won't actually execute the futures in parallel too

hiredman21:02:37

because you create them lazyily in a map, then map deref, then doall

noisesmith22:02:16

yeah, you'd usually want (run! deref (doall futures)) - unless your real code uses the value of response

hiredman22:02:20

you need the doall to force the future creation first, then deref

Mario C.22:02:50

@futuro Not following... @noisesmith I see what you are saying but in my repl the catch in the foo function does not run. Wouldn't the exception propagate up to that functions try-catch?

Mario C.22:02:06

And the real code does use the value of response

hiredman22:02:35

I would print out 'e' so you actualy lsee the exception being generated

Mario C.22:02:56

in the real code the "foo" function's catch block is what is actually being fired.

noisesmith22:02:00

@mario.cordova.862 regarding what futuro was saying there are multiple types that can be thrown, Exception is one of them, but they are all subtypes of Throwable

Mario C.22:02:31

Not the catch in the futures

noisesmith22:02:41

in particular, Error is not an Exception

futuro22:02:51

Errors and Exceptions are different kinds of objects, and both can be thrown.

Mario C.22:02:56

Is this in regards to my printlns?

noisesmith22:02:23

it's regarding the usage of the word "error" in describing your code that doesn't catch Error

hiredman22:02:24

(catch Exception e in the future doesn't catch everything

hiredman22:02:54

but the (catch Exception e in foo wouldn't catch it either

hiredman22:02:03

best print out the actual exceptions

hiredman22:02:18

they may not be what you expect, and they generally tell you exactly where they come from

noisesmith22:02:22

(ins)user=> (try (assert false) (catch Exception _ :OK))
AssertionError Assert failed: false  user/eval149 (NO_SOURCE_FILE:3)
(ins)user=> (try (assert false) (catch Error _ :OK))
:OK

hiredman22:02:43

@noisesmith he is saying that the handler in foo actual runs

Mario C.22:02:04

java.util.concurrent.ExecutionException: java.lang.Exception:

hiredman22:02:05

if I recall, some times a future will wrap an exception

Mario C.22:02:16

Yes, which is believe what is happening

hiredman22:02:18

and the rest of it

hiredman22:02:12

my guess is you are running out of memory queue futures or something

hiredman22:02:20

but the rest of the exception will tell you

noisesmith22:02:23

if j.u.c.ExcecutionException is the type of the exception something else entirely is happening, yeah...

hiredman22:02:29

to be clear, what it appears to be is not an exception from the body of the future (where the try catch is) but an exception from the machinery that executes futures

Alex Miller (Clojure team)22:02:07

setting the default uncaught exception handler can help detect weirdnesses like this

Mario C.22:02:34

#error {
 :cause Error getting number of borrowers
 :via
 [{:type java.util.concurrent.ExecutionException
   :message java.lang.Exception: Error getting number of borrowers
   :at [java.util.concurrent.FutureTask report FutureTask.java 122]}
  {:type java.lang.Exception
   :message Error getting number of borrowers
   :at [com.some.project$get_number_of_borrowers invokeStatic file-name.clj 937]}]
 :trace
 [[com.some.project$get_number_of_borrowers invokeStatic file-name.clj 937]
  [com.some.project$get_number_of_borrowers invoke file-name.clj 933]
  [com.some.project$add_fields$fn__21224$lookup_call__21225$fn__21226 invoke file-name.clj 1255]
  [new_reliquary.core$wrap_with_named_transaction$fn__20964 invoke core.clj 28]
  [new_reliquary.core.NewRelicTracer trace core.clj 32]
  [new_reliquary.core.NewRelicTracer doTransaction core.clj 35]
  [new_reliquary.core$with_newrelic_transaction invokeStatic core.clj 45]
  [new_reliquary.core$with_newrelic_transaction invoke core.clj 43]
  [com.some.project$add_fields$fn__21224$fn__21229 invoke file-name.clj 1260]
  [clojure.core$binding_conveyor_fn$fn__4676 invoke core.clj 1938]
  [clojure.lang.AFn call AFn.java 18]
  [java.util.concurrent.FutureTask run FutureTask.java 266]
  [java.util.concurrent.ThreadPoolExecutor runWorker ThreadPoolExecutor.java 1149]
  [java.util.concurrent.ThreadPoolExecutor$Worker run ThreadPoolExecutor.java 624]
  [java.lang.Thread run Thread.java 748]]}

Mario C.22:02:05

I am explicitly throwing the error though

noisesmith22:02:23

are you doing it inside somethig lazy?

hiredman22:02:36

ok, yeah, I saw the juc and jumped to a conclusion and was wrong

Mario C.22:02:44

doing what? The try catch or the throw?

noisesmith22:02:06

the throw - laziness can make exceptions escape a try/catch block

hiredman22:02:11

it is wrapped in a java.util.concurrent.ExecutionException which happens when derefing the future

Mario C.22:02:11

The throw is happening within a map, so yes?

Mario C.22:02:28

the mapping function calls another function which does the throw

noisesmith22:02:07

yeah, and the exception won't happen until that map is realized, which in some cases can make a throw "escape" from a try/catch body

noisesmith22:02:03

simple version:

(user=> (future (try (map / [1 0]) (catch Exception _ :OK)))
#object[clojure.core$future_call$reify__8097 0x7a560583 {:status :pending, :val nil}]
user=> (deref *1)
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:163)

noisesmith22:02:15

without the laziness

user=> (future (try (mapv / [1 0]) (catch Exception _ :OK)))
#object[clojure.core$future_call$reify__8097 0x1dd6d4b7 {:status :pending, :val nil}]
user=> (deref *1)
:OK

Leonardo Freitas22:02:20

Hello everyone! I am quite new to Clojure and I have a couple of questions regarding best practices / fp mindset: So I have a list of jobs (a queue if you will), and I am implementing a dequeue function. So the function receives the queue and the criteria to choose which job will be processed. Then, the function returns the queue without the job choosen, and the job choosen separated. Given that I have to return 2 separated things, what is the best approach to do this? - Return a vector with them like [queue job]? (this one I find odd, as the order will matter when handling the return). - Return a hash map like {:queue queue :job job}. This one sounds ok for me given that I come from Node.js background. - Or should I do this on two separated functions? One to find the job with the criteria (returning the job) and a second one to remove a job with the criteria (returning the updated queue), much like peek and pop?

Mario C.22:02:37

@noisesmith I think you are right. There is a lot of laziness going on here and I believe its causing the exception to escape

noisesmith22:02:34

yeah, using anything lazy requires a lot of care around "block" oriented code like try/catch or with-open etc.

manutter5122:02:59

@leonardo.freitas.s I would say either the vector or the hash map, whichever you're more comfortable with. I frequently use a vector when I need to return 2 values, because it's pretty easy to use destructuring to get the values out (let [[a b] (some-fn-that-returns-2-items-in-a-vector)] ...)

manutter5122:02:24

More than two, I'd probably go with a hash map.

didibus22:02:55

@leonardo.freitas.s There's no wrong or right, or established best practice here.

didibus22:02:04

Go with your guts

noisesmith22:02:35

@leonardo.freitas.s the two functions solution is just asking for race condition errors

Leonardo Freitas22:02:21

Awesome, I guess I will go with the hash map, makes a lot of sense for me. Thanks @didibus, @noisesmith and @manutter51!

noisesmith22:02:23

@leonardo.freitas.s by the way - what mutable container do you plan on using? or is it a purely functional version built on recur or something?

Leonardo Freitas22:02:29

Purely functional for now. Just a simple job queue that should be able to find the most adequate jobs to the given criteria

noisesmith22:02:33

in that case, you probably don't have to worry about race conditions at all as the natural way to implement that would be strictly single threaded

Leonardo Freitas22:02:30

Yeah, but two functions would also mean iterating through the queue twice

Leonardo Freitas22:02:52

So I guess hash map / vector is the best way here

noisesmith22:02:51

if your comparison function is a valid sorting comparator, you can use sorted-set-by and assume the first item in that ordered set is always highest priority

Leonardo Freitas23:02:44

Hmm, not sorting it as priority changes with every criteria. But well, tyvm for the help!

didibus23:02:15

If using a vector, removing an item from the middle of it is O(1)

didibus23:02:18

using subvec

noisesmith23:02:01

no, because you still need to reconstruct the vector

noisesmith23:02:44

using a set allows efficiently removing random items though (at the cost of no fixed order - sounds like you wouldn't want fixed order here anyway)

noisesmith23:02:11

a set would eliminate duplicates though, so you'd need to ensure that things that are distinct contained some distinct key at least

didibus23:02:35

conj is also O(1) no?

noisesmith23:02:57

O(n) for the n args after the coll

didibus23:02:02

Oh I see, there is no concat that is O(1) for vec

didibus23:02:51

That seems like, I don't know all the details of the vector, but it seems like if you can subvec in O(1), you should be able to concat in O(1) no?

noisesmith23:02:26

the tree structure of a vector means it's not that simple, AFAIK clojure vectors are not optimized for fast concat

noisesmith23:02:45

the cost of adding N items scales linearly with the size of N

didibus23:02:37

Right, so its nlogn

noisesmith23:02:32

at least it doesn't also scale with the size of the vector too

didibus23:02:39

That's interesting, I feel this is a good use case for a mutable datastructure then.

noisesmith23:02:54

or something like finger-tree that's meant for this use case

noisesmith23:02:19

or set, as long as you ensure entries that should be unique are, and stable ordering isn't needed

didibus23:02:18

Hum, ya, now that you mention it, set makes sense

didibus23:02:42

Probably better then finger-tree as well.

didibus23:02:56

Since Finger tree is Log n for split and concat

didibus23:02:01

Oh, nvm, sets are also logn

didibus23:02:55

So called pseudo constant. Not sure if Finger Tree are similarly pseudo constant log n

didibus23:02:15

Oh it is, based on wikipedia

didibus23:02:26

So ya, finger tree or set would be fastest here

Chase23:02:27

so I'm reading about for and it seems like it will be super powerful and useful but it is confusing me. This example:

(def list-of-lists [[1 2 3] [4 5 6] [7 8] [9 10] []])

(for [l list-of-lists
      x l]
  x)
;; => (1 2 3 4 5 6 7 8 9 10)
Tbh, I don't know how it's getting to that. I think I'm picturing those bindings like a let binding but then I feel like I should just get the original list-of-lists back.

noisesmith23:02:18

each binding in for's vector is an inner loop on its right hand side

noisesmith23:02:11

maybe this helps

user=> (for [x [1 2 3] y [4 5 6]] [x y])
([1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6])

noisesmith23:02:18

the literal translation of your example is "for each l in list-of-lists, and each x in each l, x"

Chase23:02:19

that example makes much more sense to me. I think I almost have an understanding of the one I posted. Let me pretend I'm in a hammock for a little bit.

noisesmith23:02:32

then there's this minor alteration to my previous example

user=> (for [x [1 2 3] y [4 5 6] z [x y]] z)
(1 4 1 5 1 6 2 4 2 5 2 6 3 4 3 5 3 6)

Chase23:02:38

I think what's getting me is "for each l in list-of lists" "l" is each inner vector? How is [l list-of-lists] telling it that?

Chase23:02:09

does for always take each binding as meaning each item in the collection?

noisesmith23:02:40

yes, except special things like :when :while and :let

Chase23:02:15

ahhh, yup. ok. I think it's clicking. thank you!

Chase23:02:35

so am i right in thinking for can be pretty darn powerful? Is it used quite a lot?

noisesmith23:02:01

yeah - also doseq is nearly identical except its body is an implicit do and it always returns nil and isn't lazy

noisesmith23:02:42

mapcat would see a lot more action if we didn't have for