Fork me on GitHub
#beginners
<
2021-04-01
>
sova-soars-the-sora01:04:42

I have a collection of maps, I want to find the maximum :timestamp across all elements...

sova-soars-the-sora01:04:26

something like (first (sort-by val > :key map)) gets the result...

aratare01:04:51

(apply max-key :timestamp your-coll-of-maps)

sova-soars-the-sora01:04:43

Neat! Can you explain how to think about the problem to get that solution?

aratare01:04:48

well from your description, it’s like max but you want some way to get that inner timestamp, so you can use max-key to supply that

sova-soars-the-sora01:04:43

yep that's exactly right. i don't really get how max-key works, but i will stare at it more

sova-soars-the-sora01:04:20

that's very clean 😄

sova-soars-the-sora01:04:47

Ah I think I get it. max-key is like max and get-in smashed together

sova-soars-the-sora01:04:21

but we need apply... because why?

aratare01:04:01

because max-key takes a number of args when you call it

aratare01:04:21

again, it’s like max

aratare01:04:38

you’d call (max 1 2 3), not (max [1 2 3])

sova-soars-the-sora01:04:51

righteous. thank you

aratare01:04:42

just think of max-key as max, but you can supply a function to calculate the value which it’ll use to find the maximum

aratare01:04:20

(= (max 1 2 3) (max-key (fn [v] v) 1 2 3) (max-key identity 1 2 3))

sova-soars-the-sora01:04:17

very illustrative example and REPL'able! thank you

👍 3
aratare01:04:12

I missed a paren at the end so if you ran it, it may not return 😅

sova-soars-the-sora01:04:21

😄 i noticed but took it as a friendly exercise to the reader

sova-soars-the-sora01:04:58

you solve the problems i balance the parens everyone feels smart 😛

😅 6
sova-soars-the-sora01:04:28

thanks a lot. if you're interested in what i'm tinkering with right now, i'm trying to apply this "send over only database deltas :since-timestamp" principle, so every so often the client says "i have up to timestamp ts-val" and the server checks the database of entries and returns things newer than that. pretty simple. i got the idea from @tonsky, "For dataset sizes up from few hundred kilobytes you start to feel need to transfer deltas instead of entire value." https://tonsky.me/blog/datomic-as-protocol/

👍 3
R.A. Porter02:04:01

You might - if you’re not married to Datomic - also want to take a look at Crux. It’s a bitemporal database that might fit your specific need well.

aratare01:04:14

I’m also playing around with datomic at the moment. Seems really cool.

aratare04:04:18

Hi there. I’m stumbling across something and I’m not sure what’s going on. I have this block of code:

(case (class (OffsetDateTime/now))
    java.time.OffsetDateTime "a"
    String "b"
    false)
and somehow that’s evaluated to false even though I’m expecting it to be true. Am I doing something wrong here? Thanks in advance.

hiredman04:04:03

case takes unevaluated constants

hiredman04:04:40

String and java.time.OffsetDateTime are symbols that evaluate to classes

phronmophobic04:04:53

The test-constants are not evaluated. They must be compile-time literals, and need not be quoted
...
All manner of constant expressions are acceptable in case, including numbers, strings,symbols, keywords, and (Clojure) composites thereof.

phronmophobic04:04:27

the short answer is you need something like cond or condp along with instance?

aratare04:04:41

Ah I see. I’ve always thought classes themselves are constants. Thanks @hiredman @smith.adriane

👍 3
Alex Miller (Clojure team)04:04:04

classes are not constants (and not even unique in the JVM - every classloader can potentially load their own version)

👍 3
Alex Miller (Clojure team)04:04:25

case on class name is occasionally useful

Alex Miller (Clojure team)04:04:42

(assuming you have a finite set of concrete classes to check)

aratare04:04:07

Thanks @alexmiller. condp gives me the expected behaviour so things are finally working for me now 😅

Endre Bakken Stovner07:04:50

Can I type hint a keyword? What is the incantation? I've tried ^clojure.lang/Keyword kw

raspasov08:04:01

(let [^clojure.lang.Keyword k :a]
  k)

Alex Miller (Clojure team)12:04:56

You shouldn’t need to do so

Endre Bakken Stovner12:04:15

Thanks! I should start looking at the Clojure internals someday.

Endre Bakken Stovner07:04:31

Same question for {}. I've tried ^java.util.Map.

raspasov08:04:36

(let [^clojure.lang.PersistentHashMap m {}]
  (assoc m :a 42))

Endre Bakken Stovner08:04:29

Uh-oh: java.lang.IllegalArgumentException: Unable to resolve classname: clojure.lang/PersistentHashMap. Might there be something wrong with my setup?

Endre Bakken Stovner08:04:01

No. I wasn't using all dots.

Endre Bakken Stovner08:04:06

Why the difference?

raspasov08:04:06

It’s a Java class; forward slashes are for accessing static methods

raspasov08:04:20

(System/currentTimeMillis)

raspasov08:04:36

Why exactly do you need to type hint?

raspasov08:04:44

Generally, it shouldn’t be necessary

raspasov08:04:01

It’s needed mostly for performance reasons.

Endre Bakken Stovner08:04:27

The below function will - worst case - be called umpteen gazillion times:

(defn to-path [^String file ^clojure.lang.Keyword rule ^clojure.lang.PersistentHashMap wildcards]
  (let [rulename (name rule)
        wc (zipmap (map name (keys wildcards)) (vals wildcards))
        wc (->> wc sort flatten (clojure.string/join "/"))]
    (clojure.string/join "/" [rulename wc file])))
So I am hoping type-hinting will improve the performance a little.

raspasov08:04:50

Also, to be “correct” type hint in all cases, use clojure.lang.APersistentMap:

(instance? clojure.lang.PersistentHashMap {})
=> false
(instance?
  clojure.lang.PersistentHashMap
  (into {} (comp (partition-all 2) (map vec)) (range 100)))
=> true
(instance?
  clojure.lang.APersistentMap
  {})
=> true
(instance?
  clojure.lang.APersistentMap
  (into {} (comp (partition-all 2) (map vec)) (range 100)))
=> true

raspasov08:04:25

(type {}) => clojure.lang.PersistentArrayMap

raspasov08:04:40

PersistentArrayMap is an optimization for small maps;

🙏 3
raspasov08:04:59

I wouldn’t bother with such things unless you’re really chasing performance; if you really care about performance, use YourKit (but it’s a paid tool); you can almost never guess where your bottleneck is; it’s probably where you least expect it.

Endre Bakken Stovner08:04:27

But this is a cheap optimization (in terms of readability and programmer effort) which might matter a lot.

🙂 3
Alex Miller (Clojure team)12:04:18

Those type hints won’t do anything

Alex Miller (Clojure team)12:04:22

Type hints are only useful in turning an ambiguous java interop call into an unambiguous one (to avoid reflection). There are no interop calls in this method, so those type hints are useless and ignored

Endre Bakken Stovner12:04:59

Had no idea! Thanks

Alex Miller (Clojure team)12:04:29

you generally never should make an interop call on types that are part of Clojure (you should use normal Clojure invocations to interact) and thus it’s very unusual to type hint Clojure classes

Alex Miller (Clojure team)12:04:36

If you were to type hint a Clojure map, the correct interface is IPeraistentMap (not the APersistentMap abstract class - not all maps extend from that)

🙏 3
👆 3
👍 3
raspasov12:04:58

re: interfaces, thanks for correcting my ignorance 🙂 @alexmiller

Aron09:04:08

I think I know the answer, but would be nice to know definitely: if I have map with unqualified keywords, can I write a spec for those values mapped by those unqualified keywords? Or if I want values to be validated I need to put them in maps with qualified keywords?

delaguardo10:04:51

(s/keys :req-un [::alias/foo] :opt-un [::alias/bar]) with (s/def ::alias/foo …) and (s/def ::alias/bar …) will be able to validate maps like this {:foo … :bar …}

Aron10:04:49

Thanks, not sure why I missed this when reading the docs, I could find it now easily.

rafalw11:04:48

Hi, I have java lib with constructor which i have to use, and this constructor requires instance of java's ExecutorService as an argument, what wil be the best way to use this lib from clojure?

arielalexi13:04:38

Hi 🙂 I want to use browse-url function on a windows OS in order to rederact to diffrent page. Is it possible to convert .URL to clojure.java.browse ?

delaguardo13:04:23

(str …) ?

3
arielalexi13:04:29

yes that worked. (browse-url (str ( test-meow))) tnx :)

David Reno15:04:51

As far as I know, there’s no function like partial that can return a function given all of the arguments so I’m using def with an anonymous function like this:

(def fancy-now #(str "the time is now " (new java.util.Date)))
  (fancy-now)
Is this the right way to do this? Like the example, my function is an “action” in that it depends on external conditions.

David Reno15:04:47

I know it works, I’m just wondering if it’s idiomatic or if I’m missing a bigger picture idea.

agile_geek15:04:35

Can’t you just define a fn that takes no args and then invoke it?

(defn fancy-now []
  (str "the time is now " (new java.util.Date)))

agile_geek15:04:48

Or if you want to define it in-line:

(defn my-fn-that-uses-now []
  ....
  (str "the time is now " (new java.util.Date))
  ....)

agile_geek15:04:20

Or if you want to you can ‘store’ the result in a var in a let:

(defn my-fn-that-uses-now []
  (let [now (str "the time is now " (new java.util.Date))]
     (fn-needing-now now)
     ....)

agile_geek15:04:18

As an example say you have a fn that returns a map that needs to include the results of that str you might do something like this:

(defn my-fn-that-uses-now []
     ....
     {:name name
      :age 23
      :now (str "the time is now " (new java.util.Date))})

David Reno15:04:47

ok, my example wasn’t complex enough for what I’m doing but your comments helped me sort it out. It’s more like the following (which I can now use partial for): (defn fancier-now [greeting time] (str greeting "the time is now " time)) (def fanciest-now (partial fancier-now "Hello, ")) (fanciest-now (new java.util.Date)) In my real use-case, I can’t pass in (new java.util.Date) because I’m actually doing this for dependency injection and I don’t want fanciest-now to need to know about java.util.Date.

agile_geek15:04:03

I don’t think I understand why you need the def for fanciest-now or the context of the invocation of fanciest-now so hard to give advice. Also dependency injection is not something we really do in Clojure. Tend to just use fn’s passed around in data if required. However, not sure if it’s useful but you can define a high order fn that returns a fn if required like so:

(defn fancier-now
  [greeting]
  (fn [time]
    (str greeting  "the time is now " time)))
(let [fanciest-now (fancier-now "Hello, ")]
  (fanciest-now (new java.util.Date)))

agile_geek15:04:38

I used the let there simply to define the var for the anonymous fn returned from fancier-now but I could have used a def like you did i.e. (def fanciest-now (fancier-now "Hello, "))

agile_geek15:04:38

I think there are probably more idiomatic ways to accomplish what you are trying to do but I don’t have enough context to suggest any.

David Reno16:04:22

Sorry for the slow replies, I have to think about the suggestions and my use case. The context seems somewhat complex to me so I didn’t want to trouble anyone with it but here’s as short as I can describe it: • I’m interfacing a chip via i2c to read environment data • I’m waiting for interrupts to know something changed which I read via gpio • when there’s a gpio interrupt, I read the i2c chip for data • I have a namespace for using gpio to information-hide those details • I have a namespace for i2c to information-hide those details • Since this is all asynchronous in nature, I have to run this all from an async/go routine So the code goes something like this: (def chip-context (chip/setup-chip …)) (async/go (gpio/handle-events chip/event-handler chip-context deserializer event-channel …)) So when I say I’m “injecting dependencies” I only mean I’m passing in functions and data to the handler. This whole question is oriented around how I can pass the handler function and chip-context data in a way that seems coherent. I was taking the approach of partially applying the handler to already have the chip context, deserializer and output channel but maybe a map is better.

delaguardo17:04:15

Is there some trick to get in macro in which stage it been used?

(defmacro foo []
  (cond
    cljs? 'cljs.core.async
    clj? 'clojure.core.async))
something like this ^

delaguardo17:04:00

maybe stage is the wrong term here 🤷 but I don’t know the right one

delaguardo17:04:15

so far I found the difference in &env between clj and cljs but not sure if this is something that I can rely on

Noah Bogart17:04:58

are you looking for reader conditionals? https://clojure.org/reference/reader#_reader_conditionals

#?(:clj     Double/NaN
   :cljs    js/NaN
   :default nil)

delaguardo17:04:35

not really, I have macro.clj where I define some macro and app.cljc where I want to use it. But the code generated by the macro should be different depending on target of compilation (clj or cljs)

delaguardo17:04:34

so far I added this

~(if (contains? &env :js-globals)
   'cljs.core.async/go-loop
   'clojure.core.async/go-loop)
but it looks too ugly

delaguardo17:04:27

and I think the fact that there is :js-globals in &env is not really reliable

dpsutton17:04:10

can help with lots of this stuff

dpsutton17:04:22

and you can read the source to just implement in that manner or use the helpers

leif21:04:22

Could someone point me to where I can find the difference between <! and <!! in core/async? I'd google it myself but its kind of hard to get google to not ignore that syntax.

leif21:04:40

And the api seems to be identical except the workd parking and blocking.

seancorfield21:04:42

One is blocking, the other is not.

leif21:04:32

@seancorfield But the 'non-blocking' one says: "Will park if nothing is available."

seancorfield21:04:39

Parking is not blocking.

seancorfield21:04:05

Blocking will block a thread. Parking just switches to another go block to continue executing something else.

leif21:04:19

Okay, what is parking? (I was under the impression that parking was like blocking but for green-threads.)

leif21:04:15

> Blocking will block a thread. Parking just switches to another `go` block to continue executing something else. Hmm...so what happens then when it comes back to the original task that 'parked', and still didn't have something available?

leif21:04:25

doesn't*

seancorfield21:04:34

It won’t unpark until there is something to take.

seancorfield21:04:49

go blocks are “collaborative” in that sense.

leif22:04:42

Ah, wait, by parking, vs blocking, are you meaning: "parking = freezing a go-thread" and blocking = freezing an os thread"?

seancorfield22:04:04

No. Parking doesn’t freeze anything.

leif22:04:15

And if so, then it seems like clojurescript wouldn't have <!! , at least not without webworkers, since there would be only one os-thread.

Franco Gasperino22:04:33

There is an implied event loop, which is effectively a big queue of functions which are cooperatively yielding control to the thread driving the event loop

Franco Gasperino22:04:01

the go macro is similar to the javascript await, python await, etc

Franco Gasperino22:04:25

execute to <here>, then yield control from this current execution back to the event loop so something else can run

Franco Gasperino22:04:30

the "park" is similar to "yield execution control back to the event loop. wake me up when something on the other side of the park operation has placed something in that channel i'm waiting on, and wake me up after that"

seancorfield22:04:47

I’m pretty certain ClojureScript does not have <!! or >!! @U050G2TQJ

leif22:04:02

AH, okay, this makes sense.

leif22:04:21

@U38AEDBQU That also makes a lot of sense.

Franco Gasperino22:04:28

the main benefit is that in either concurrent async or parallelism threading mode, you are using a queue idiom that works the same on both

Franco Gasperino22:04:20

with the channel (chan) being the queue

seancorfield22:04:48

Well, if all your go blocks are parked, then it “freezes” (until another thread changes the state of the channels in a way that something can unpark).

seancorfield22:04:11

The TL;DR is that core.async is really hard to use correctly and can be a pit of vipers for beginners 😐

leif22:04:07

> Well, if all your `go` blocks are parked, then it “freezes” (until another thread changes the state of the channels in a way that something can unpark). I mean, that sounds an awful lot like blocking to me. I suspect I'm missing something here.

ghadi22:04:17

blocking == a thread is tied up

ghadi22:04:03

parking == no thread tied up (there is a callback attached to a channel; when the callback fires, the go block is resumed)

ghadi22:04:21

by thread I mean a real java.lang.Thread

leif22:04:32

@ghadi Okay, so then ya.

leif22:04:45

For clojurescript you wouldn't ever use <!! without webworkers.

leif22:04:58

(And parking is blocking for green-threads.)

leif22:04:04

(Which are cooperative threads.)

leif22:04:14

Or at least I'm meaning that.

seancorfield22:04:42

I think it’s misleading to think of core.async even as “green threads”. It doesn’t have lightweight threads of any description (in ClojureScript): it has collaborating sequential processes (CSP of old).

seancorfield22:04:00

(in the Clojure version, go blocks run in their own small thread pool as I recall)

leif22:04:43

Mmm...fair.

leif22:04:19

Okay, I think I have my answer now though, thanks. ^.^

leif22:04:35

TLDR, use <! in clojurescript, as its the only option. 🙂

leif22:04:53

Because it only has one os thread (again, sans webworkers)

hiredman22:04:08

you can use take!, but you have to pass it a callback

leif22:04:07

Ah, well given that my goal is to await on a function that takes a callback, probably not quite what I want.

leif22:04:35

(I'd use <p!, but its not a promise, its just a CPSed function.

alpox22:04:34

Is it a javascript ES7 await that you need? In that case you could go with plain-js and use the promise constructor to turn the function taking a callback into a javascript promise that you can await.

leif22:04:55

@alpox Using something like Promise.resolve?

alpox23:04:53

That would create a promise that instantly resolves with a value. I was talking about https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise the constructor

leif23:04:45

Ah, okay.

alpox23:04:28

The pattern for wrapping a callback function would look about something like this:

(defn do-something-async [param-1]
  (js/Promise.
    (fn [resolve reject]
      (my-fn-with-callback
        param-1
        (fn [err result]
          (if err
            (reject err)
            (resolve result)))))))

👁️ 3