clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
NoahTheDuke 2021-04-09T01:42:28.252100Z

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

seancorfield 2021-04-09T01:55:08.253200Z

@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…

👍 1
seancorfield 2021-04-09T01:55:33.254100Z

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

NoahTheDuke 2021-04-09T01:57:15.255700Z

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 ...))

seancorfield 2021-04-09T02:00:29.257200Z

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?

seancorfield 2021-04-09T02:04:14.258300Z

@nbtheduke The “official” Sente example app has a stub for login/auth https://github.com/ptaoussanis/sente/blob/master/example-project/src/example/server.clj

👍 1
NoahTheDuke 2021-04-09T02:16:44.259300Z

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

2021-04-09T02:25:58.259500Z

A websocket is a socket, so a stateful bidirectional connection

seancorfield 2021-04-09T03:16:36.260400Z

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.

jmckitrick 2021-04-09T13:01:15.264700Z

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....

jmckitrick 2021-04-09T13:01:49.265400Z

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.

jmckitrick 2021-04-09T13:02:22.266100Z

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

jmckitrick 2021-04-09T13:02:25.266300Z

So...

jmckitrick 2021-04-09T13:03:01.266700Z

(ring-io/piped-input-stream
                                (fn [ostream]
                                  (let [^java.io.Writer w (<http://clojure.java.io/make-writer|clojure.java.io/make-writer> ostream {})]
                                    (get-big-data-streaming w))
                                  (.close ostream)))

jmckitrick 2021-04-09T13:03:17.267100Z

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

jmckitrick 2021-04-09T13:03:24.267300Z

And the function of interest:

jmckitrick 2021-04-09T13:04:23.267600Z

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

jmckitrick 2021-04-09T13:05:02.268300Z

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

jmckitrick 2021-04-09T13:05:57.268800Z

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

jmckitrick 2021-04-09T13:07:42.269300Z

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

aratare 2021-04-09T13:08:59.270Z

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

jmckitrick 2021-04-09T13:09:07.270200Z

ok

byrongibby 2021-04-09T13:43:31.275300Z

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

nilern 2021-04-09T13:52:53.276300Z

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

nilern 2021-04-09T13:53:33.277100Z

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

💡 2
🙁 1
nilern 2021-04-09T13:57:58.278200Z

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

byrongibby 2021-04-09T14:03:03.278400Z

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?

byrongibby 2021-04-09T14:04:44.278900Z

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]))

byrongibby 2021-04-09T14:06:16.279100Z

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.

nilern 2021-04-09T14:13:19.279300Z

Now I remember, you have to do it like this https://github.com/nilern/monnit/blob/main/src/monnit/pair.cljc#L11-L26

nilern 2021-04-09T14:13:54.279600Z

An unfortunate gotcha

byrongibby 2021-04-09T14:22:51.279900Z

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

🙇 1
2021-04-09T16:46:59.283200Z

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

nilern 2021-04-09T17:00:05.283400Z

Check out https://github.com/ptaoussanis/tufte

❤️ 1
ghadi 2021-04-09T17:06:54.284500Z

@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}))

❤️ 2
nilern 2021-04-09T17:07:05.284600Z

&amp;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

ghadi 2021-04-09T17:07:28.285100Z

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

nilern 2021-04-09T17:08:23.285400Z

&gt; (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#))

nilern 2021-04-09T17:09:17.285800Z

Also note the use of nanoTime instead of currentTimeMillis

➕ 3
dpsutton 2021-04-09T17:10:07.286100Z

that's super close to just a prepl

2021-04-09T17:12:54.287600Z

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

👍 1
1
danielglauser 2021-04-09T17:30:31.288900Z

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ý 2021-04-09T18:18:37.291200Z

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?

✅ 1
Jakub Holý 2021-04-09T18:20:40.291300Z

I would ask in Zulip in https://clojurians.zulipchat.com/#narrow/stream/151924-data-science

1
2021-04-09T18:20:52.291500Z

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ý 2021-04-09T18:21:41.292300Z

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

Jakub Holý 2021-04-09T18:22:28.292500Z

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ý 2021-04-09T18:22:53.292700Z

Fun fact: In Norwegian, do means toilet.

😂 6
Darin Douglass 2021-04-09T18:23:13.293Z

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

🚽 2
seancorfield 2021-04-09T18:30:30.293500Z

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.

👍 1
seancorfield 2021-04-09T18:32:30.294Z

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

Jakub Holý 2021-04-09T19:06:10.294600Z

Thank you for sharing that!

danielglauser 2021-04-09T19:45:23.295200Z

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

2021-04-09T19:46:08.295400Z

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

1
2021-04-09T20:03:40.295800Z

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

2021-04-09T20:03:54.296Z

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

2021-04-09T20:05:09.296200Z

do doesn't exist

😮 1
2021-04-09T20:06:22.296500Z

(macroexpand '(when true 3))
;;=&gt; (if true (do 3))

2021-04-09T20:06:47.296700Z

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

2021-04-09T20:07:09.296900Z

that makes sense. I think

2021-04-09T20:07:41.297100Z

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

martinklepsch 2021-04-09T21:15:33.297500Z

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

yuhan 2021-04-10T08:18:03.315Z

another golfey solution: 🙂

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

👍 1
Linus Ericsson 2021-04-09T21:22:07.298100Z

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

2
1
martinklepsch 2021-04-09T21:22:38.298300Z

wow

martinklepsch 2021-04-09T21:22:51.298500Z

that is very cool 😅

martinklepsch 2021-04-09T21:23:12.298700Z

also, I always forget about cycle

martinklepsch 2021-04-09T21:23:29.298900Z

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

martinklepsch 2021-04-09T21:23:35.299100Z

thanks @oscarlinusericsson!

Linus Ericsson 2021-04-09T21:23:49.299300Z

Thanks! index space is the shit.

😄 1
kenny 2021-04-09T21:58:27.300100Z

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

ghadi 2021-04-09T22:05:45.300500Z

negative

dpsutton 2021-04-09T22:15:32.300700Z

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

1
vncz 2021-04-09T22:20:52.301Z

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

🙏 1
kenny 2021-04-09T22:54:41.301300Z

Nice