Fork me on GitHub
#clojure
<
2021-04-09
>
Noah Bogart01:04:28

is there a good place to talk about libraries like sente? i have some architecture questions. i saw #ring but that doesn't seem to cover web sockets

seancorfield01:04:08

@nbtheduke I don’t see any more specific channels so you could ask here. At least once folks see the question, they’ll know whether there’s a better channel for it…

👍 3
seancorfield01:04:33

(I haven’t used Sente for years but I can take a stab at it maybe)

Noah Bogart01:04:15

i'm wondering about how to handle authorization in sente handlers. As an example, using Compojure for http requests lets me say: (wrap-routes admin-routes auth/wrap-authorization-required), and then in wrap-authorization-required, i can say (if (:admin user) (handler req) (response 401 ...))

seancorfield02:04:29

I can’t remember whether Sente exposes header information but I seem to recall we tackled this somehow, back in the day. I think we passed some sort of auth token back to the server?

Noah Bogart02:04:44

Oh, maybe I've completely misunderstood then. So the authentication happens when the websocket connection is made, not at each message sent?

hiredman02:04:58

A websocket is a socket, so a stateful bidirectional connection

seancorfield03:04:36

If you have a clustered setup and you lose connectivity to one server and failover to another, you have to reestablish the auth'd status on the new server. Something to thing about.

jmckitrick13:04:15

I'm dipping my toes in the 'next-jdbc' waters, and ran into a small issue. It's probably a gap in my knowledge of streaming, but here goes....

jmckitrick13:04:49

I have a query using 'plan' that then can be reduced 'into []' with no issue.. it's a huge result set, which is what I expect.

jmckitrick13:04:22

But when I try to reduce that into something streamable, I'm getting an empty result..

jmckitrick13:04:01

(ring-io/piped-input-stream
                                (fn [ostream]
                                  (let [^java.io.Writer w ( ostream {})]
                                    (get-big-data-streaming w))
                                  (.close ostream)))

jmckitrick13:04:17

This is in the handler ☝️:skin-tone-2:

jmckitrick13:04:24

And the function of interest:

jmckitrick13:04:23

(reduce (fn [^java.io.Writer w record]
              (let [m (datafiable-row record *db* {})]
                (.write w m)
                ))
            writer
            (jdbc/plan *db* sv))

jmckitrick13:04:02

We can safely assume that sv is a sqlvec that works and generates a large dataset outside of this streaming code

jmckitrick13:04:57

So the 'w' passed in is the Writer and locally is named writer

jmckitrick13:04:42

Hitting the endpoint takes about 4 seconds, the same as the non-streaming version, but returns no data.

aratare13:04:59

@jmckitrick Not exactly the answer you’re looking for, but for anything next.jdbc-related you can also ask in #sql

byrongibby13:04:31

Hi. I am trying to implement a protocol for a type I am defining as part of clojure.core.matrix

mp/PFunctionalOperations


  (element-map!
      ([m f]
        (.modifyAll (.-array1d m) (UnaryFn. f)))
      ([m f a]
        (.modifyMatching (.-array1d m) (BinaryFn. f) (.-array1d a)))
      ([m f a more]
        (.modifyMatching (.-array1d m) (BinaryFn. f) (.-array1d a))
        (doseq [b more] (.modifyMatching (.-array1d m) (BinaryFn. f) (.-array1d b))))) 
The class UnaryFn implements a functional Java interface allowing me to pass in clojure.lang.IFn to method .modifyAll which expects a Java lambda. When I test this functionality on its own it works perfectly, but when I include it in the protocol implementation it fails (see below). Can anyone help me understand why?
; --------------------------------------------------------------------------------
; eval (current-form): (def m (create-vector [1 2 3]))
#'ojalgo-clj.core/m
; --------------------------------------------------------------------------------
; eval (selection): m
(1.0 2.0 3.0)
; --------------------------------------------------------------------------------
; eval (current-form): (.modifyAll (.-array1d m) (UnaryFn. inc))
nil
; --------------------------------------------------------------------------------
; eval (selection): m
(2.0 3.0 4.0)
; --------------------------------------------------------------------------------
; eval (current-form): (deftype Vector [^Array1D array1d] Object (toSt...
; (err) Syntax error macroexpanding clojure.core/let at (C:\Git\ojalgo-clj\src\clojure\ojalgo_clj\core.clj:18:1).
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: simple-symbol? at: [:bindings :form :local-symbol] spec: :clojure.core.specs.alpha/local-name
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: vector? at: [:bindings :form :seq-destructure] spec: :clojure.core.specs.alpha/seq-binding-form
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-bindings
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-special-binding

nilern13:04:53

That looks like a syntax error related to something else than the matrix or UnaryFn stuff per se

nilern13:04:33

As an aside, run! and the underlying reduce are faster than doseq

💡 6
🙁 3
nilern13:04:58

It seems like you accidentally put (.modifyAll (.-array1d m) (UnaryFn. f)) into a binding position but I don't see anything like that in your initial snippet

byrongibby14:04:03

Thanks for the run! tip. So if I take out the arity overloading like so

(element-map!
      [m f]
        (.modifyAll (.-array1d m) (UnaryFn. f))) 
It compiles. So it has to do with the binding introduced by the multiple arities?

byrongibby14:04:44

If I have

(element-map!
      ([m f]
        (.modifyAll (.-array1d m) (UnaryFn. f))))
It macro expands to
(element-map!
   [p__18280 p__18281]
   (clojure.core/let
    [[m f] p__18280 (.modifyAll (.-array1d m) (UnaryFn. f)) p__18281]))

byrongibby14:04:16

Which in turn leads to

; eval (current-form): (deftype Vector [^Array1D array1d] Object (toSt...
; (err) Syntax error macroexpanding clojure.core/let at (C:\Git\ojalgo-clj\src\clojure\ojalgo_clj\core.clj:19:2).
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: simple-symbol? at: [:bindings :form :local-symbol] spec: :clojure.core.specs.alpha/local-name
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: vector? at: [:bindings :form :seq-destructure] spec: :clojure.core.specs.alpha/seq-binding-form
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-bindings
; (err) (.modifyAll (.-array1d m) (UnaryFn. f)) - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-special-binding
when evaluated.

nilern14:04:54

An unfortunate gotcha

byrongibby14:04:51

Aha! Thanks, I was looking at an extend-protocol example which works with the parens. Thank you very much for the help.

🙇 3
stopa16:04:59

Hey team, I want to create a handy help to time and log other function calls.

(defmacro with-logging [k body]
  `(let [start-ms# (System/currentTimeMillis)
         res# ~body
         end-ms# (System/currentTimeMillis)]
     (log/infof {:k ~k :ms (- end-ms# start-ms#)})
     res#))
So, to use this, I could do something like :
(defn get-foo [id] (with-logging :get-foo (db/get ["select * from foos where id = ?" id])))
One pesky thing though, is that I need to repeat myself here: :get-foo is the same as the fn def name Is there a way you would change this, so this is not necessary? Am thinking of creating a macro, but what I have in mind seems a bit too involved

nilern17:04:05

&env does not give the fn name as a local so you would need a defn-like macro You could use (.. Thread currentThread getStackTrace) but that might trigger deoptimization, not good for profiling

ghadi17:04:54

@stopachka one useful strategy is to capture the form/expression itself so you don't have to synthesize a name

(defmacro timing [expr]
  `(let [s# (System/currentTimeMillis)
         ret# ~expr
         time# (- (System/currentTimeMillis) s#)]
     {:ret ret#
      :time time#
      :expr '~expr}))

❤️ 6
ghadi17:04:28

repl> (timing (Thread/sleep 5000))
{:ret nil, :time 5002, :expr (Thread/sleep 5000)}

nilern17:04:23

> (source time)
(defmacro time
  "Evaluates expr and prints the time it took.  Returns the value of
 expr."
  {:added "1.0"}
  [expr]
  `(let [start# (. System (nanoTime))
         ret# ~expr]
     (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
     ret#))

nilern17:04:17

Also note the use of nanoTime instead of currentTimeMillis

9
dpsutton17:04:07

that's super close to just a prepl

hiredman17:04:54

(defn t [f k] (let [n (System/nanoTime)] (k (f) (/ (- (System/nanoTime) n) 1e6))))

3
👍 3
danielglauser17:04:31

If you wanted to implement a user or content driven recommendation system in a JVM/Clojure/Postgres project what libraries would you look at? Cortex?

Jakub Holý (HolyJak)18:04:37

Hi folks! I believe I have seen the recommendation to use when instead of if when I only care about the truthy branch, i.e. (when cond someting) instead of (if cond someting) . I wanted to refer to it but could not find it. Am I just making it up?

3
jjttjj18:04:52

I remember a recentish discussion on this as well. I found https://www.reddit.com/r/Clojure/comments/3x44qx/style_question_and_if_when/ but I think it was brought up more recently though I can't remember where

Jakub Holý (HolyJak)18:04:41

Thanks a lot! I was looking into the guide but just could not see it 🙂

Jakub Holý (HolyJak)18:04:28

No, actually the guide says > Use when instead of (if …​ (do …​)). I just skipped that because here I did not have a do 🙂

Jakub Holý (HolyJak)18:04:53

Fun fact: In Norwegian, do means toilet.

😂 18
Darin Douglass18:04:13

all the more reason to use it liberally through-out your codebase!

🚽 6
seancorfield18:04:30

FWIW, Phil Hagelberg (technomancy) has pretty strong opinions about this: he thinks when should only be used for side-effecting things and that a single-branch if for pure code is just fine. I think he still holds that anyway. Certainly, there was a very pointed commit to Leiningen some years back that replaced when with if in a lot of places.

👍 3
seancorfield18:04:30

(I don’t agree with that but it sticks in my mind as a specific preference from a well-known Clojurian)

Jakub Holý (HolyJak)19:04:10

Thank you for sharing that!

danielglauser19:04:23

@U04V70XH6 The irony is I believe the Sonian team (where Phil used to work) would reject any PR that had a single branch if. @U0NCTKEV8 do you remember if that was the case?

hiredman19:04:08

I don't recall that, but I do recall lots of spirited debate

3
Jeff Evans20:04:40

is there any extra overhead incurred by having the implicit do?

Jeff Evans20:04:54

or does that get optimized away when it’s clear there is only one form?

hiredman20:04:09

do doesn't exist

😮 3
jjttjj20:04:22

(macroexpand '(when true 3))
;;=> (if true (do 3))

hiredman20:04:47

it exists in clojure, but doesn't exist in bytecode

Jeff Evans20:04:09

that makes sense. I think

hiredman20:04:41

it is a mechanism for expressions, bytecode isn't expressions

martinklepsch21:04:33

I’m trying to figure out a good way to create a lazy list of vectors in this shape:

[[0 0 0]
 [1 0 0]
 [1 1 0]
 [1 1 1]
 [2 1 1]
 [2 2 1]
 [2 2 2]]
(basically increments on an item by item basis) — i have something roughly working but it’s not particularly nice so curious what other ideas people may have

Linus Ericsson21:04:07

(take 10 (reductions (fn [a b] (update a b inc)) [0 0 0] (cycle (range 3))))

rich2 3
rich 6
martinklepsch21:04:51

that is very cool 😅

martinklepsch21:04:12

also, I always forget about cycle

martinklepsch21:04:29

but probably I wouldn’t have come up with this approach either way 😄

Linus Ericsson21:04:49

Thanks! index space is the shit.

😄 3
vncz22:04:52

Freaking awesome. It’d have been taking me years to come up with such solution @UQY3M3F6D

🙏 3
em00:04:18

@U050TNB9F Here's a cute code-golf-y solution: (->> (range) (map #(quot % 3)) (partition 3 1) (map reverse) (take 10)) That said, while this is slightly more succinct, I much prefer @UQY3M3F6D’s version as it conveys the semantics of the intended operation much better.

👍 3
yuhan08:04:03

another golfey solution: 🙂

(for [x (range)]
  (for [i [2 1 0]]
    (quot (+ x i) 3)))

👍 3
kenny21:04:27

Is there a way to get the second to last error in your REPL?

dpsutton22:04:32

(def ^:dynamic *e2)
(binding [*e2 nil]
  (clojure.main/repl :caught (fn [e]
                               (set! *e2 *e)
                               (clojure.main/repl-caught e))))

notbad 3