Fork me on GitHub
#clojure
<
2021-11-26
>
richiardiandrea13:11:36

Hi all! Does anybody use Google Guava with Clojure? In particular, I am thinking of using their https://github.com/google/guava/wiki/RangesExplained api and I would like to know if there is any Clojure wrapper out there already (or if it's not worth having it).

Ben Sless14:11:14

What use case are you aiming at? You can wrap Clojure's range with with some set-like behavior to get both

richiardiandrea15:11:59

I need to basically check if my ranges are contiguous and/or have overlaps.

Ben Sless15:11:45

You're adding them up in some manner then checking?

Ben Sless15:11:48

You could use interval trees

richiardiandrea16:11:25

yeah well I have intervals and I basically need to check for existing gaps

richiardiandrea16:11:55

I actually really like how they are showing at the REPL

(interval->guava-range i1)
#object[com.google.common.collect.Range 0x56a8dbf7 "[1.0..10.0)"]

richiardiandrea16:11:34

If I understand correctly interval trees are trees that have intervals as keys?

richiardiandrea16:11:49

(will google more, thanks for the hint)

richiardiandrea16:11:45

yeah it does not fulfill my use case, we have open/closed bounds (in Guava called endpoints) but thanks for the link!

Ben Sless17:11:28

You can also start from something like this

(defn -range
  [{:keys [min max]}]
  (let [^clojure.lang.Range r (range min (inc max))]
    (reify
      clojure.lang.IPersistentSet
      (contains [_ k] (and (< k max) (< min k)))
      clojure.lang.IChunkedSeq
      (chunkedFirst [_] (.chunkedFirst r))
      (chunkedNext [_] (.chunkedNext r))
      (chunkedMore [_] (.chunkedMore r))
      clojure.lang.ISeq
      (first [_] (.first r))
      (next [_] (.next r))
      (more [_] (.more r))
      (cons [this o]
        (if (.contains this o)
          this
          ???)))))

emccue18:11:13

i’d say not worth having it for your problem. probably worth someone doodling up a cljc library if someone else has the same problems

richiardiandrea20:11:54

I know I could implement this myself but Google Guava fits the bill and it's reliable - I just wished it was packaged as separate jar so that I could use only what I need

emccue21:11:24

yeah - the eternal conundrum of Apache/Guice

👍 1
restenb14:11:22

i'm curious about the (potential) differences between run!, doseq, and mapvwhen processing lists of side-effecty things.

Ben Sless15:11:10

doseq and run! don't return anything, while mapv does. doseq has a bit of a DSL to support conditions and nested loops, run is straightforward

restenb15:11:49

i have this HTTP connection which returns data intermittently over time. since the response class implements Iterable, i can seqover it to process the data when it's available. but i'm seeing some curious differences between these three options.

Ben Sless15:11:44

That's odd, what sort of differences are you seeing?

restenb15:11:08

there's some logging in there when data comes in. with mapvand run I see the log immediately as expected. with doseqi only see everything at the end, when the connection is terminated (this is on a several minute interval).

Ben Sless15:11:48

Sounds like chunking. I don't recall off the top of my head but I think calling seq on iterable creates a chunked seq

borkdude15:11:13

What would be a good name for the key :example/key in {:example/keys [a]} - we already have :keys-destructuring true for a to indicate that keyword was used for keys destructuring, but we need a name for the other one too

1
hanDerPeder18:11:04

Bikeshedding. When writing an api I quite like the pattern of a function that takes a map and assoc’s its result onto it under some sensible key as return value. Alternatively you could just return the result and let the caller add it if desired. Any preferences?

👍 1
pavlosmelissinos18:11:23

It's often convenient and definitely depends on the case but personally I'm not sure it's good design. Like you said, why not return just the value you're changing and let the consumer do whatever they want (assoc or something else)?

👍 1
seancorfield18:11:08

It depends on how the function is expected to be used. I find returning an augmented map works really well for functions in a threading pipeline. But if a function is intended to be used standalone, a straightforward return value makes sense a lot of the time.

seancorfield18:11:13

For example, at work we have a number of image-processing functions that are intended to be threaded, so they all return their input hash map with "results" assoc'd in.

hanDerPeder18:11:26

Yes, I’m assuming the function is useful in a threading pipeline. My main problem is this:

(-> some-map
    some-operation
    (assoc :some-key (some-func ??)))
I don’t know of any clean way to reference the output map of some-operation there. Maybe at-> would work here, but I’ve always steered away from it.

borkdude18:11:51

Also, when I'm not sure about future extensions of the argument and result value, in my experience, taking and returning a map is the best bet.

pavlosmelissinos19:11:39

Maybe I'm overthinking it but my "worry" is that computing the value and enriching a map might be a kind of complecting. Like, maybe it would be better to have x functions that each computes a value and an extra function that does just the enriching by calling the others and assoc'ing their result. But yeah I agree that returning a map is often more convenient!

1
borkdude19:11:59

There's also a performance consideration. Very low level functions might not have the luxury of creating maps for each results.

borkdude19:11:12

So it kind of depends, but in most cases it's probably ok

seancorfield19:11:19

(-> some-map
    ...
    (as-> m (assoc m :some-key (some-operation m))
    ...)
If that seems like a "common" pattern with certain functions, I'd say it's a good argument for making them return a hash map of their input + their output.

seancorfield19:11:17

Or at least writing a wrapper function: (defn with-result [m k f] (assoc m k (f m))) so you can do:

(-> some-map
    (with-result :some-key some-operation)
    ...)

2
🙂 1
👍 1
hanDerPeder19:11:53

I like the with-results function. When your first reaction is ‘I should have though of that’ you know it’s good 🙂

reefersleep08:11:45

Generally, I think it’s nicer not to assume any composition in your value-producing fn, but let your callers do the composition. I don’t have a no hard rule, though.

Sidestep20:11:44

what happened to clojure.tools.deps.alpha.repl ?

Sidestep20:11:55

repl is not there anymore?

Sidestep20:11:08

trying to hotload a lib

Sidestep20:11:49

with add-libs

Sidestep20:11:46

(require '[clojure.tools.deps.alpha.repl :as deps])

Sidestep20:11:06

returns ; (err) Could not locate clojure/tools/deps/alpha/repl__init.class, clojure/tools/deps/alpha/repl.clj or clojure/tools/deps/alpha/repl.cljc on classpath.

thumbnail20:11:43

Work on add-lib can be found here; https://github.com/clojure/tools.deps.alpha/tree/add-lib3 I don't think it was ever on master :thinking_face:

Sidestep20:11:00

probably not

Sidestep20:11:37

I assumed it was as I saw it in Seans' presentation on clojure repl development (clojures' superpower)

Sidestep20:11:55

thanks

1
👍 1
Jack Park21:11:03

Revising my thinking: defprotocol implemented as defrecord. Need to know how to call that record; There is a gist https://gist.github.com/KnowledgeGarden/2dc4d57ac59ba856584fb59f206e957e which is the state of the code - which varies; depending on which code example or document you read, you either need a "this" or you don't. I have yet to nail down that heuristic. But, the primary issue is making the API's put-proxy visible to call from elsewhere. Right now, it's like a mock trial: no real proxy or database (which will be Datahike) is in play. Trying to sort this out.

Jack Park21:11:12

Modified the code in core.clj to make an instance of the record: `

(defn foo
  []
  (let [db (DhPgBackside "database")]
    (db/put-proxy  "foo"))
  (println "Hello, World!"))
` at which point Intellij insisted that the class DhPgBackside could be imported, so I allowed that; now it gets a ClassNotFound exception (even if I put (:gen-class) in that file.

lukasz15:11:55

I left a comment - maybe that clarifies things up. Btw, naming the namespace and record class with the same identifier is really confusing, that's why your IDE is confused perhaps. The comment: https://gist.github.com/KnowledgeGarden/2dc4d57ac59ba856584fb59f206e957e#gistcomment-3976260

Jack Park15:11:41

@U0JEFEZH6 that solved the issue and, what's more, gave me a much clearer picture of what's going on than I could find in books. Many thanks 👍

mateus.henrique.brum21:11:42

Hello, I would like if there is a clever way to implement this…

(defn keys-sequence [ks]
  (let [index (ref -1)]
    (fn [] 
      (let [next (dosync (ref-set index (inc (deref index))))]
        (nth ks next)))))

(def ks (keys-sequence [1 2 3 2 1]))

;;(ks) => 1
;;(ks) => 2
;;(ks) => 3
;;(ks) => 2
;;(ks) => 1
It is not exactly like (cycle [1 2 3 2 1]) as it is not a lazy sequence, it is just a function with state, any idea of a already existent function for that?

Ben Sless21:11:20

Simpler, I think

(defn pull
  [^Iterable coll]
  (let [it (.iterator coll)]
    (fn [] (.next it))))

Ben Sless21:11:53

you'll need to check that the iterator is over and stop consuming somehow

Ben Sless21:11:12

like so

(defn pull
  [^Iterable coll]
  (let [it (.iterator coll)]
    (fn [] (when (.hasNext it) (.next it)))))

Ben Sless21:11:54

Now the obvious question, why?

Noah Bogart22:11:45

@UK0810AQ2 can you do that without Java interopt

seancorfield22:11:09

How about this with an atom?

dev=> (defn keys-sequence [ks]
 #_=>   (let [data (atom ks)]
 #_=>     (fn [] (let [[[x] _] (swap-vals! data next)] x))))
#'dev/keys-sequence
dev=> (def kf (keys-sequence [1 2 3 2 1]))
#'dev/kf
dev=> (kf)
1
dev=> (kf)
2
dev=> (kf)
3
dev=> (kf)
2
dev=> (kf)
1
dev=> (kf)
nil
dev=> 

seancorfield22:11:12

Technically, that's [[x & _] _] -- swap-vals! returns the old value and the new one, and you only care about the first element of the old value.

seancorfield22:11:02

I guess this would be nicer:

dev=> (defn keys-sequence [ks]
 #_=>   (let [data (atom ks)]
 #_=>     (fn [] (ffirst (swap-vals! data next)))))

seancorfield22:11:37

@U14K4HW3T You don't need ref unless you are trying to coordinate multiple updates in a single "transaction". ref is almost never needed, to be honest.

mateus.henrique.brum23:11:27

@UK0810AQ2 to emulate a sequence of line-read in a test.

mateus.henrique.brum23:11:06

@U04V70XH6 cool, the last code is much better, thanks for share it. One question, isn’t var a better option in this context? Just a fake sequence for a unit test…. Thanks…

seancorfield23:11:19

What do you mean "var"?

Ben Sless05:11:44

@UEENNMX0T possible, but outside of cljc compatibility I'd reach for an iterable here

mateus.henrique.brum11:11:28

@U04V70XH6 something like this:

(defn keys-sequence [ks]
  (def index -1)
  (fn []
      (let [next (alter-var-root (var index) inc)]
        (nth ks next))))

Ben Sless11:11:47

I'd say you shouldn't write your code to begin with in such a manner which is coupled to line read. You have an initial state and a series of inputs. Do you need to model the source of inputs for it to work? (f [state line]) -> new-state Then you can even test this flow in a loop or reduce. Separate the source of inputs

mateus.henrique.brum13:11:08

Yep, I guess my problem is to understand simple concepts. Just for illustrate metaphorically the conceptual problem. Imagine I have a simple calculator, and it accumulate values like this.

(defn calculator [ns] 
  (reduce + 0 ns))
How can I work with an infinite but lazy source in ns? ex: '(input-line, input-line, … , input-line) and terminate when the reduce value is x? For this I have created a recursive function, like:
(defn calculator-fn [acc, n]
  (if (= acc 10)
    acc
    (recur (+ acc (first acc)) (read-line)))) 
I am not sure how really extract the read-line and treat that as a regular collection so use it as ns in the first example. Notice that (read-line) is infinite here an will terminate when (= acc 10) Did you get the whole ideia? who use reduction with a streams of data..!? Thanks.

mateus.henrique.brum13:11:08

Okay, after some studies, I realise I can do it this way:

(defn acc[a c]
  (let [sum (+ a c)]
    (if (>= sum 10)
      (reduced sum)
      sum)))

;; user real inputs
(reduce
 acc
 0
 (map #(Integer/parseInt %) (repeatedly read-line)))

;; unit test
(reduce
 acc
 0
 (range 9 11))
Any way to improve it? Any way to weed out the acc function? Maybe transducer, but not sure yet …

seancorfield20:11:21

Re: your "var" example -- having def inside a function is pretty much always wrong. And treating Clojure Vars like mutable "vars" from other languages is definitely not the approach you should be considering -- it's very non-functional.