Fork me on GitHub
#beginners
<
2023-03-16
>
Jakub Šťastný00:03:30

How do I pretty-print an exception the same way Clojure prints exceptions by default? I found https://clojuredocs.org/clojure.stacktrace, I believe it's what does it, but I can't load it:

(require '(clojure.stacktrace))
nil
clojure.stacktrace/print-throwable
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
Context: I caught an exception, I want to print a custom message first and then pretty-print the exception.

seancorfield00:03:34

(require 'clojure.stacktrace) -- not sure why you have parens around it?

seancorfield00:03:56

build=> (require 'clojure.stacktrace)
nil
build=> clojure.stacktrace/print-throwable
#object[clojure.stacktrace$print_throwable 0xebfa509 "clojure.stacktrace$print_throwable@ebfa509"]
build=> (doc clojure.stacktrace/print-throwable)
-------------------------
clojure.stacktrace/print-throwable
([tr])
  Prints the class and message of a Throwable. Prints the ex-data map
  if present.
nil

Jakub Šťastný00:03:23

I see. I nearly always use it with ns, so I got confused.

seancorfield00:03:24

If I require with parens like your example, I get that same exception (but that's not legal require syntax, right?)

Jakub Šťastný00:03:42

I was confused that it returned nil.

Jakub Šťastný00:03:54

OK, that explains 🙂 Thanks @U04V70XH6

seancorfield00:03:11

You can (require '[clojure.stacktrace]) as a vector...

seancorfield00:03:05

(~/clojure)-(!2002)-> clj
Clojure 1.11.1
user=> (require '[clojure.stacktrace])
nil
user=> clojure.stacktrace/print-throwable
#object[clojure.stacktrace$print_throwable 0x4acf72b6 "clojure.stacktrace$print_throwable@4acf72b6"]
user=>

Wed Mar 15 17:23:44
(~/clojure)-(!2003)-> clj
Clojure 1.11.1
user=> (require '(clojure.stacktrace))
nil
user=> clojure.stacktrace/print-throwable
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
clojure.stacktrace
user=>

Jakub Šťastný00:03:27

Yeah that's it, I confused the vector syntax.

John00:03:48

what am I missing about wanting existing instances to have updated behavior after a deftype/defrecord ?

John00:03:13

maybe I should me using defmulti/defmethod instead of relying on deftype and protocols? or not?

John00:03:57

I want to evolve the implementation of my protocol without rebuilding my instances which contain state like network connections

John01:03:39

maybe the usual repl flow is to map->Class after reevaluating the defrecord ?

Ben Sless06:03:54

Let's start with - why use records and types to begin with in your use case?

John13:03:57

to implement a protocol ensuring the abstractions are used, and to have runtime polymorphism

Ben Sless14:03:02

What sort of thing are you abstracting over? Data or state and operations? Even with protocols you have several options, including extend-protocol and extension via metadata It's rare to need new concrete types in Clojure If it's just data, multimethods may be better Also see https://github.com/cemerick/clojure-type-selection-flowchart

John14:03:08

A component that I want to be able to swap for another implementation or a mocking implementation. It started as just immutable data but is evolving to state, by keeping maps to connect implementation behaviors to abstractions inside the app's domain. I had started with deftype and moved now to defrecord, but yeah I'm still a bit lost

Ben Sless14:03:45

Why not just use function as an interface?

Ben Sless14:03:19

Also,regarding swappable implementation, have you seen the Component library?

John15:03:04

I've seen it from a distance, haven't dug in at all. With defmulti ? Right maybe that's it. Though then I wonder how I'd "ensure" that all implementations implement the full thing

John15:03:45

maybe I just need to give up that line of thinking of locking things down

John15:03:36

sorry I've been too abstract -- to make this more concrete, imagine a communication abstraction over IRC and Matrix

John15:03:46

but regardless, imagining whatever use-case that for sure would require deftype for example, I wonder how people keep the REPL quick redefinition dev approach given the instances are not updated

John15:03:06

that's what I'm mostly curious

Ben Sless15:03:53

In that case multimethods are usually used You can also take apart building messages and sending them, then have a single client protocol with a single method (send) and a multimethod for building messages of different types

Ben Sless15:03:51

Make protocols for what can be locked down early, you identified something interesting

John12:03:38

Hmmmm okay. Makes sense, as I don't know what I want the interface to be like yet. I wonder if it's not a common practice also to have deftypess (as an example) that end up just proxying calls to isolated functions, so implementations remain uncoupled. I guess in the end there's no difference, except the proxying would make redefining easier, so probably not. Thanks for reaching back.

Ben Sless14:03:28

Deftypes are rare, we usually Just Use Functions (TM) until we have a good reason not to

quan xing08:03:59

which function process data like this in clojure

(def f [fn1 fn2 fn3 ... ]) 
(apply-fun f data)  like this (-> data fn1 fn2 fn3)

lispyclouds08:03:48

id pretty much use what you said ->. dont see much of a value in having something more

delaguardo08:03:47

look at comp

delaguardo09:03:09

((comp fn3 fn2 fn1) data)

phill09:03:28

reduce is handy if the list of functions should be treated as data..

(def f [vector list])
(def data 42)
(reduce 
  (fn [a f] (f a)) 
  data 
  f) ;; => ([42])

quan xing09:03:17

@U0HG4EHMH Thanks. That's what I want

delaguardo09:03:44

comp can be used with a list of functions:

((apply comp (reverse f)) data) ;; => ([42])

😀 4
quan xing09:03:22

amazing @U04V4KLKC This code is even less:+1:

Δημήτριος Φκιαράς12:03:46

Hi, I have this function which converts euler angles (yaw, pitch and roll - if we refer to an aircraft) to a quaternion (which is a 3d rotation representation form). The function performs some calculations and then passes the value to a function from another namespace to construct the quaternion. It works ok. Is this a correct way to write such functions in Clojure (purely mathematical)? Is there a more idiomatic way?

(defn euler->quat
  "Yaw, Pitch, Roll order (ZYX) - angles in degrees.
   Output in x y z w quaternion."
  [yaw pitch roll]
  (let [y (degrees->rad yaw)
        p (degrees->rad pitch)
        r (degrees->rad roll)
        cos-y (Math/cos (/ y 2))
        cos-p (Math/cos (/ p 2))
        cos-r (Math/cos (/ r 2))
        sin-y (Math/sin (/ y 2))
        sin-p (Math/sin (/ p 2))
        sin-r (Math/sin (/ r 2))
        w (+ (* cos-r cos-p cos-y) (* sin-r sin-p sin-y))
        x (- (* sin-r cos-p cos-y) (* cos-r sin-p sin-y))
        y (+ (* cos-r sin-p cos-y) (* sin-r cos-p sin-y))
        z (- (* cos-r cos-p sin-y) (* sin-r sin-p cos-y))]
    (q/quaternion x y z w)))

Martin Půda12:03:23

(defn euler->quat
  "Yaw, Pitch, Roll order (ZYX) - angles in degrees.
   Output in x y z w quaternion."
  [& args]
  (let [ypr (mapv #(/ (m/to-radians %) 2) args)
        [cos-y cos-p cos-r] (mapv m/cos ypr)
        [sin-y sin-p sin-r] (mapv m/sin ypr)
        ...]))
m is clojure.math . But I would check the speed of both, if you care about performance.

❤️ 2
dgb2313:03:29

I actually like your code @U04R06XSJV7. It looks very clear and reads nicely.

dgb2313:03:45

making the let bindings on top more compact like @U01RL1YV4P7 does, makes sense to me as well. It's a bit more compact and more nicely grouped. But your initial code looks perfectly fine to me.

❤️ 2
genmeblog13:03:35

My 2cents about performance: destructuring kills it, type hinting helps a lot. You may turn on (set! *unchecked-math*** :warn-on-boxed) to verify if you work on primitives (certainly not). Regarding the code structure - for me it's really good and clean.

genmeblog13:03:27

Maybe you can divide y, p and r by 2.0 at the moment of defining them.

❤️ 2
genmeblog13:03:30

(defn degrees->rad
  ^double [^double degrees]
  (/ (* Math/PI degrees) 180.0))

(defn euler->quat
  "Yaw, Pitch, Roll order (ZYX) - angles in degrees.
   Output in x y z w quaternion."
  [^double yaw ^double pitch ^double roll]
  (let [y (/ (degrees->rad yaw) 2.0)
        p (/ (degrees->rad pitch) 2.0)
        r (/ (degrees->rad roll) 2.0)
        cos-y (Math/cos y)
        cos-p (Math/cos p)
        cos-r (Math/cos r)
        sin-y (Math/sin y)
        sin-p (Math/sin p)
        sin-r (Math/sin r)
        w (+ (* cos-r cos-p cos-y) (* sin-r sin-p sin-y))
        x (- (* sin-r cos-p cos-y) (* cos-r sin-p sin-y))
        y (+ (* cos-r sin-p cos-y) (* sin-r cos-p sin-y))
        z (- (* cos-r cos-p sin-y) (* sin-r sin-p cos-y))]
    (q/quaternion x y z w)))

Sam Ritchie14:03:29

Emmy takes the approach of writing code using multimethods, but then compiling down to a fast representation for end uses

❤️ 2
Δημήτριος Φκιαράς18:03:20

Thanks, your opinions are invaluable to me!

pppaul00:03:22

if you want speed, the data science clojure group (on slack and more on zulip) can probably help, as they are familiar with libs that use the GPU for math (eg: Neanderthal)

❤️ 2
Δημήτριος Φκιαράς18:03:06

Will check that out, thank you!

George Peristerakis16:03:55

Hi everyone, I'm playing around with a mongodb collection (using congomongo) where one of the document's keys is @context . The documents are returned as a list of maps

(-> documents first keys)
;; => (:description :_id :validThrough :employmentType :datePosted #viewer-eval (keyword "@context") :jobLocation :hiringOrganization :title :skills :experienceRequirements :identifier #viewer-eval (keyword "@type"))
My question: is how do reference @context?

seancorfield16:03:59

It's probably convenient to def a few aliases:

(def at-context (keyword "@context"))
(def at-type (keyword "@type"))

... (get doc at-context) ... (get doc at-type) ...

seancorfield16:03:48

Or you could just do that "in-place": ((keyword "@context") doc) ;=> (:@context doc) ;=> value of @context field.

George Peristerakis16:03:23

It worked! Thanks! 😀

Abhi Saxena19:03:33

Hi All,

(defn get-all-benchmarks [context cost-id area-code filters use-cache?]
  (let [filters-with-identifiers (assoc filters "client-id" (get-in context [:context :client-id])
                                                "cost-id" cost-id
                                                "area-code" area-code)
        services [#(try (get-country-benchmarks context
                                                (-> (assoc filters-with-identifiers "srv" "country-benchmarks")
                                                    to-map-key
                                                    object-to-str) cost-id area-code filters
                                                use-cache?)
                        (catch Exception e
                          (throw (ex-info (.getMessage e)
                                          {:cause :bad-data}))))
                  #(try (get-site-benchmarks context
                                             (-> (assoc filters-with-identifiers "srv" "site-benchmarks")
                                                 to-map-key
                                                 object-to-str) cost-id area-code filters
                                             use-cache?)
                        (catch Exception e
                          (throw (ex-info (.getMessage e)
                                          {:cause :bad-data}))))
                  #(try (get-study-benchmarks context
                                              (-> (assoc filters-with-identifiers "srv" "study-benchmarks")
                                                  to-map-key
                                                  object-to-str) cost-id area-code filters
                                              use-cache?)(catch Exception e
                                                           (throw (ex-info (.getMessage e)
                                                                           {:cause :internal-server-error}))))]]
    (->> services
         (mapv (fn [service]
                 (async/thread
                   (service))))
         (async/merge)
         (async/reduce conj [])
         (async/<!!)))
  )
the code above has 3 different service (in separate threads) even though any one of them fails with exception that final result is still being constructed and exception doesn't bubble up, since it's in different thread. How can I make sure that exception is thrown by breaking the flow.....

seancorfield20:03:45

One option is for those services to return the (ex-info ..) result instead of throwing it, then somewhere in your processing of the results, see if you got (instance? Exception result) and if so, (throw result)

seancorfield20:03:56

(Exceptions as "just" values so you can pass them around)

seancorfield20:03:51

But since you're doing everything async up until the <!!, you probably won't be able to throw overall until after you have that vector of results.

Abhi Saxena04:03:34

thanks for your response Sean

Jakub Šťastný22:03:32

Is there a way to do what`clojure.set/intersection` , preferably without having to require clojure.set and converting what I have (in lists) into sets? Like it's not a big deal, but if there'd be a short(er) version that works with lists (or vectors), would be cool.

phill23:03:59

Well, if you had ONE set and one vector, you could use (filter some-set the-vector) because Clojure sets work as a lookup function. (Which vectors do not; it would encourage linear searching.) Given two vectors, you had better convert one (maybe the smaller one) to a set in order to get fast lookups. If your vectors are so short that you don't care about fast lookups, then conversion to a set will also be a non-concern. So: (filter (set vector-a) vector-b) ... in short, you don't need clojure.set for intersection.

Jakub Šťastný23:03:34

Whaaaat? That's wild!

Jakub Šťastný23:03:47

Exactly what I was looking for. Cheers @U0HG4EHMH!

Jakub Šťastný23:03:55

Clojure doesn't stop to surprise me 🙂

phill23:03:50

> Whaaaat? That's wild! The Clojure experience, in a nutshell!

Jakub Šťastný23:03:30

Hahaha yeah. I'm loving it! But my head spins like crazy at times that's for sure.

Jakub Šťastný23:03:06

Had to do some JS (which I normally like), but after Clojure I was like what? Syntax? F*ck that!

dgb2323:03:46

(let [coll [[:a :b] [:b :c] [:d :b]]]
  (reduce (comp set filter)
          (-> coll first set)
          (rest coll)))

dgb2323:03:22

JS sets are cool. Unfortunately [1, 2] != [1, 2]

Jakub Šťastný23:03:38

I'm on CLJS for all that now 🙂 Starting a new project, it has a tight deadline, my CLJ skills aren't great (but I get by), I was considering it and was like I want to work with CLJ no matter what, so here I am! Moonlighting to learn, to fill in gaps in my skills, but really loving it.

🎉 2
pppaul00:03:59

I find that requiring clojure.set doesn't come up too often for me, and I use the set functions a lot, just not in most namespaces. I think set/intersection is more informative than the filter example, which is more like a clojure idiom.

Jakub Šťastný00:03:37

Hahaha totally, but I appreciate the short version.