Fork me on GitHub
#beginners
<
2022-06-23
>
sova-soars-the-sora03:06:13

Is there a way to change #object[org.whatever.thing$function 0x4371abdd "org.whatever.thing$function 0x4371abdd"] into just org.whatever.thing/function?

seancorfield03:06:02

Can you provide a bit more context? Do you have the actual function value or do you have that resultant string -- the result of printing the function value?

seancorfield03:06:13

As for turning the string at the center in that into the symbol you want:

user=> (clojure.repl/demunge "org.whatever.thing$function")
"org.whatever.thing/function"
user=>

seancorfield03:06:59

@U3ES97LAC So is this one "string" per line in that file?

seancorfield03:06:59

Seems like you could do a clojure.string/replace to pull out the class name of the function value and then run it through demunge as shown above.

sova-soars-the-sora03:06:47

actually it's an .edn file, and this is one of the values in a map

seancorfield03:06:45

Ah, so you'd have to use the tagged-literal format to read it in and then you'd have a vector of a symbol (whose name you could demunge), the hex value, and the string.

sova-soars-the-sora03:06:13

tagged literal like #'thing ?

seancorfield03:06:06

Hmm, I thought you might have to use clojure.core/tagged-literal but you can just provide a reader for object.

seancorfield03:06:33

user=> (def edn "{:value #object[foo.bar$quux 0x12345 \"foo.bar$quux 0x12345\"]}")
#'user/edn
user=> (require '[clojure.edn :as edn])
nil
user=> (edn/read-string {:readers {'object identity}} edn)
{:value [foo.bar$quux 74565 "foo.bar$quux 0x12345"]}
user=>

seancorfield04:06:03

Then you can just walk the data structure and transform matching values using demunge

seancorfield04:06:49

But you could use a fancier reader function than identity to do the transform too...

seancorfield04:06:07

Like so:

user=> (def edn "{:value #object[foo.bar$quux 0x12345 \"foo.bar$quux 0x12345\"]}")
#'user/edn
user=> (require '[clojure.edn :as edn])
nil
user=> (edn/read-string {:readers {'object (fn [[sym _n _s]] (clojure.repl/demunge (name sym)))}} edn)
{:value "foo.bar/quux"}
user=>

👁️ 1
seancorfield04:06:37

You'll want to turn it into a symbol and call requiring-resolve on it to get the Var for redefinition.

hiredman03:06:20

Then how would you tell the difference in printed output between the symbol org.whatever.thing/function and that function object?

sova-soars-the-sora03:06:17

I have saved these strings to a file and want to use with-redef to redefine those functions with a saved result.

Jim Strieter05:06:44

How hard would it be to write a macro that mimics Haskell's guard syntax? For instance, I would like to write functions like this:

(defn f [a b c]
  | (= a 42) (run-this-if-a-equals-42 a b c)
  | (= b 99) (run-me-if-b-equals-99 a b c)
  | otherwise (run-me-if-nothing-else a b c))
Has anyone done this?

seancorfield05:06:00

That sounds pretty straightforward. You'd have some defn-like macro that partitioned forms up by | and turned it into a big cond function call...

Jim Strieter05:06:53

Thank you so much @U04V70XH6! I will look into that! 🙂

seancorfield05:06:16

(defn f [a b c]
  (cond (= a 42)
        (run-this-if-a-equals-42 a b c)
        (= b 99)
        (run-me-if-b-equals-99 a b c)
        :else
       (run-me-if-nothing-else a b c))))
That's the equivalent code, right?

💯 1
hiredman05:06:36

There are also libraries that do pattern matching to one degree or another

seancorfield05:06:24

user=> (defmacro defnh [name args & clauses]
  (let [blocks (partition 3 clauses)
        cond-code-blocks (mapcat #(if (= 'otherwise (first %)) [:else (second %)] %) (map rest blocks))]
    `(defn ~name ~args (cond ~@cond-code-blocks))))
#'user/defnh
user=> (defnh f [a b c]
| (= a 42) (println 'a-was-42)
| (= b 1)  (println 'b-was-one)
| otherwise (println 'just a b c))
#'user/f
user=> (f 42 43 44)
a-was-42
nil
user=> (f 40 1 44)
b-was-one
nil
user=> (f 40 41 44)
just 40 41 44
nil
user=>

🙌 1
seancorfield05:06:57

(that's very simplistic -- you may want something "smarter")

Jim Strieter05:06:35

What might I want to make smarter?

Jim Strieter05:06:49

(I've never written a macro before so I'm dumb)

seancorfield06:06:50

If the above serves your needs, use it. If you find a case it doesn't support, you'll need to "make it smarter". I don't know Haskell's syntax well enough to say what you'll need 🙂

seancorfield06:06:38

Frankly, I'd just write the cond in the first place... I don't particularly like Haskell's syntax for that (or many other things).

seancorfield06:06:19

As @U0NCTKEV8 said, you might want core.match syntax in there but... ugh... I wouldn't.

Jim Strieter06:06:56

I don't blame you for not liking Haskell. It's a weird animal to be sure.

Jim Strieter06:06:01

Okay making it smarter as needed makes sense. I didn't know if there's some common off nominal one should account for from the beginning.

seancorfield06:06:34

The first rule of Macro Club is "don't use macros" so... 🙂

👍 1
Carlo08:06:00

@U014Z9N3UTS I'll add my two cents as a fellow haskeller: by all means do this to better understand how macros work in clojure and to experiment, but I'd suggest not to rely on this kind of haskellism when you write clojure code. The cond version @U04V70XH6 shared is perfect. It is idiomatic and when in the future you'll want some kind of macro that does code analysis, having preserved a simple syntactic structure will repay you thousandfold.

tomd10:06:53

Why do I need gensym outside of the quoted form, rather than using auto-gensym # syntax?

(defmacro recursive-macro-1 [[x & more :as xs] acc]
  (if (seq xs)
    `(let [x# ~x]
       (recursive-macro-1 ~more (conj ~acc x#)))
     acc))

(defmacro recursive-macro-2 [[x & more :as xs] acc]
  (if (seq xs)
    (let [gx (gensym 'x)]
      `(let [~gx ~x]
         (recursive-macro-2 ~more (conj ~acc ~gx))))
     acc))

(comment
  (recursive-macro-1 [1 2] []) ;; => [2 2]
  (recursive-macro-2 [1 2] []) ;; => [1 2]
  )
I would expect both version to result in [1 2] but the first one seems to fail to scope the let binding as I'd like. (Obvs these macros are simplified to the point that they don't need to be macros - the conditional logic in the real version isn't relevant here)

delaguardo10:06:04

defmacro itself is a macro so you can expand the declaration of recursive-macro-1 to debug

👍 2
jumar11:06:02

It might be more helpful to start with macroexpand-1 and macroexpand-all If you do macroexpand-1, all is good:

;; auto-gensym
(let [x__10885__auto__ 1]
  (recursive-macro-1 (2) (conj [] x__10885__auto__)))

;; gensym
(let [x11094 1] 
  (recursive-macro-2 (2) (conj [] x11094)))
But with macroexpand-all you will notice that auto-gensym produces only one identical symbol and shadows itself:
;; auto-gensym - the name of the local binding is the same in the nested let*
(let* [x__10885__auto__ 1]
  (let* [x__10885__auto__ 2]
    (conj (conj [] x__10885__auto__) x__10885__auto__)))

;; gensym
(let* [x11095 1]
  (let* [x11096 2] 
    (conj (conj [] x11095) x11096)))

tomd11:06:20

ah yes - was just doing this myself. so this isn't a bug, it's just something to be aware of when using auto-gensym?

jumar11:06:46

Probably 🙂. I wasn't aware of this either - I rarely write macros...

1
delaguardo11:06:08

Doesn't look like a feature to me. Auto-gensym should work as expected and give you unique symbols every time it is used. I can't point a finger to the problem in the first macro, debugging is needed)

jumar11:06:24

Just in case, I tried this with clojure 1.10.3, 1.6.0, 1.3.0, and 1.0.0 and got the same behavior.

tomd11:06:21

nice. now I think about it, a question about auto-gensyms in a recursive macro may have been better suited to #clojure than #beginners anyway 😆 - I'll post something on ask clojure when I get a sec. thanks for both of your help investigating

2
😃 1
Alex Sky14:06:59

Hey folks, can I use macros in Tagged Literals? I know problem in var, but is it possible to get around it somehow? or is it a bad idea in general

Alex Miller (Clojure team)14:06:51

can you explain more about why or what you want to do with a macro?

Alex Miller (Clojure team)14:06:25

a tagged literal reader should be a function that takes some read inputs and returns a value

Alex Miller (Clojure team)14:06:48

so that is functionally at odds with what a macro is (a function of code->code)

🙌 1
Alex Sky14:06:44

@U064X3EF3 I would like to implement a similar mechanism as the reader conditionals. And more to highlight it in code so it’s not just a list or something. #variety(:foo [1 2 4] :bar [3 4]) Thank you for your reply.

emccue14:06:33

You should be able to just use a regular macro for that (variety :foo [1 2 4] :bar [3 4])

Alex Sky14:06:33

> You should be able to just use a regular macro for that (variety :foo [1 2 4] :bar [3 4]) (edited) Okay, I’m aware of that. But regular macros don’t stand out from normal code or a simple function and I would like to highlight such places specifically. If it’s possible and not difficult I know roughly how to implement it, but it’s so complicated and not worth it))

emccue15:06:57

I think this one is tricky

emccue15:06:21

so this can work pretty easily #variety (:foo 1 :bar 2)

emccue15:06:54

but i'm not sure how something like this would work

emccue15:06:20

(let [a 1
      b 2]
  #variety (:foo a :bar b))

emccue15:06:48

the result of a tagged literal isn't interpreted again as code, so you would end up with the literal symbol a and not 1

Alex Miller (Clojure team)15:06:08

the reader does not have arbitrary reader macros (tagged literals are intentionally not that)

👍 1
mbjarland15:06:48

umm...so if I have a map with namespaced keywords, is there a way for me to retrieve a value from the map if I don't know the namespace but do know the rest?

Ferdinand Beyer16:06:06

Of course, but not as efficient:

user=> (filter #(= "x" (name (key %))) {:foo/x 1, :bar/y 2})
([:foo/x 1])

Ferdinand Beyer16:06:34

..and of course you can get multiple results

Mark Wardle17:06:23

Another option: (-> {:my/id 1 :my/name "Mark" :my/dog "Ollie"} (update-keys name) (get "dog")) => "Ollie"

mbjarland15:06:28

nevermind, I'll just assume that is not easy by design as the keys might collide otherwise...

mariownyou16:06:58

I need to resize image but my code is not working. Can someone help me please. I’m new in clojure and java, i use go and python as my main languages. This is my solution: |

(defn resize
  "Resize a file.  Pass in a width and height."
  [file-in file-out width height]
  (let [img     (javax.imageio.ImageIO/read (io/file file-in))
        imgtype (java.awt.image.BufferedImage/TYPE_INT_ARGB)
        simg    (java.awt.image.BufferedImage. width height imgtype)
        g       (.createGraphics simg)]
    (.drawImage g img 0 0 width height nil)
    (.dispose g)
    (javax.imageio.ImageIO/write simg "jpeg" (io/file file-out))))

Martin Půda17:06:37

I think that jpeg images can't have an alpha channel (= TYPE_INT_ARGB). Try this:

(defn resize
  "Resize a file.  Pass in a width and height."
  [file-in file-out width height]
  (try (let [img (ImageIO/read (io/file file-in))
             simg (BufferedImage. width height BufferedImage/TYPE_INT_RGB)
             g (.getGraphics simg)
             scaled (.getScaledInstance img width height Image/SCALE_SMOOTH)]
         (.drawImage g scaled 0 0 width height nil)
         (ImageIO/write simg "jpeg" (io/file file-out)))
       (catch Exception e (prn e))))

👍 1
mariownyou17:06:37

thanks, this is working))))

practicalli-johnny22:06:07

I'm building a REST API as an integration point to some backend and external systems. Assuming I have an incoming request that about a product, 3 external services and one internal service need to be contacted to respond to the incoming request. I assume I would start with a handler that fires off a future fore each service to reply to and see if there were any issues I assume I could also use core.async to capture the responses from each service, especially if there is a retry for services that are timing out too quickly. I may be able to do some processing of the results as each individual response from a service is received, instead of waiting for the complete set of responses. Anyone have any tips or approaches that worked well or I haven't considered yet? Thanks

zugnush01:06:04

I'm doing something similar but without the fan out complexity. Sorry no ideas there. My plan for next week was to search for anything that might automate testing from the swagger spec I have. I'd be interested to hear if you have any plans along those line. BTW I've spent quite a bit of time watching and reading your guides on various topics. As a solo developer I've appreciated it very much!

👍 1
valtteri06:06:55

Promesa is an alternative for futures / core.async. As there are many possible states, conditions etc I'd consider clj-statecharts to model them.

👍 1
valtteri06:06:11

Promesa doesn’t provide retries oob but it’s not hard to implement them with it

w08r06:06:08

As for the resilience side of things. I've not used the wrapper but the underlying lib is great and may save reinventing the wheel https://github.com/ylgrgyq/resilience-for-clojure

👍 1
jumar07:06:00

For parallel processing, if it's needed at all, I'd start with plain futures or https://github.com/clj-commons/claypoole (can use a dedicated thread pool) and see if that's work well.

👍 1