Fork me on GitHub
#clojure
<
2022-08-09
>
pinkfrog05:08:12

I have a java function from a 3rd party lib, e.g., (my-func) which returns an object of type SomeType. That type has a close method. Now I want to wrap that object, but override its close method. What approach can I take? proxy?

pinkfrog05:08:58

hmm. Proxy is also a no go. It modifies the class, but not the class instance.

Mark Wardle06:08:46

Could you use deftype? Implement Closeable. Just use a plain function (close-xxx)? If this is something used as part of configuration, could you use Integrant (or similar) and do whatever you need in the halt method?

jumar06:08:00

You should probably wrap the object and delegate all method calls except close to the underlying object. Is SomeType an interface or a concrete class?

pinkfrog06:08:06

> Is SomeType an interface or a concrete class? A concrete class.

jumar07:08:30

Then can you use proxy to instantiate an object of SomeType and make it delegate all the stuff except close to the wrapped object?

pinkfrog07:08:29

Isn’t proxy wraps a class instead of an instance?

jumar07:08:34

With proxy you make an instance of SomeType and implement it in the way that it passes all the calls to the wrapped object except the close method call.

pinkfrog07:08:56

I wonder how you wrap an object with proxy.

jumar07:08:51

So a minimal example would be something like:

(let [filename "empty.zip"
      original-zip (ZipFile. filename)
      proxy-zip (proxy [ZipFile] [filename]
                  ;; overriding close method
                  (close [] (println "Ignored!")))]
  [[(.getName proxy-zip) (.getName original-zip)]
   ;; check the REPL - you will see "Ignored!" printed one time
   [(.close proxy-zip) (.close original-zip)]])

jumar07:08:21

@UGC0NEP4Y does this approach work for you or you hit some issues?

pinkfrog14:08:03

@U06BE1L6T Thanks for the code. Take the Zip class for example, if https://github.com/jumarko/clojure-experiments/blame/master/src/clojure_experiments/java.clj#L101 the filename is not given, so the only thing accessible original-zip, what can we do?

jumar20:08:59

How can the filename (or similar information required by the constructor) be not given? If you didn't know that, you couldn't construct the original object.

pinkfrog01:08:10

> I have a java function from a 3rd party lib, e.g., (my-func) which returns an object of type SomeType. It’s hidden in the 3rd party lib inside the function my-func.

jumar05:08:46

If you are not able to construct the object then I'm afraid this is not possible. In most cases, there will be a constructor which you can use and some kind of "getter" to get the information you need from the object. Is this is an open source library? It would be great if you could provide a code sample.

pinkfrog05:08:18

The problem was addressed to use another lib and the problem has gone. I was trying to understand the possibility.

Mikko Harju11:08:52

Just wondering – why does the clojure docs for < and similar say they "return non-nil if nums..." – in what case does it not return something else than true if the condition holds?

Mikko Harju11:08:48

Even clojure.lang.Numbers have boolean marked as the return value for lt and others

Joshua Suskalo13:08:15

This is the general pattern for Clojure functions, when you expect a boolean it will specify "non-nil" instead of "true", because this allows the contract to not be broken if the actual return value is changed down the line. In the specific case of the math comparison operators, there's not likely to be any change there, but many functions that might conceptually return a boolean return an object because of the pattern of returning "the most useful thing". e.g. or and and in other languages might return only a boolean, but in Clojure they return more valuable values.

👍 1
Mikko Harju14:08:30

Yes, for or, and et al I understand this completely, but for math ops it was not clear. 🙂

Alejandro11:08:46

I've just read on reddit that cognitect is against pattern matching, which is controversial. I can't find any discussion of this. Is there any? Or maybe a hint of why? I got used to it in erlang. Why would pattern matching work less great in clojure, at least readability-wise?

Jan K12:08:11

Rich Hickey has objections to pattern matching in the "Simple Made Easy" talk.

👍 1
Ed12:08:56

I think it's less about readability and more about coupling. I think that any time you have a case/cond/etc you create a branching code structure that is closed for extension. Pattern matching (in a lot of languages) is often a dispatch-on-type kind of operation, and often dispatching on types that you don't own. This sounds a lot like polymorphism and could perhaps be better served using protocols or multimethods. I've not done a lot of erlang, so I don't feel in a position to comment on that vs something like core.match ... but I think the point is more about questioning if you're using the right language construct for the job rather than a hard and fast rule 😉

👍 1
Alejandro12:08:55

@U06BE1L6T, thanks, this should be pinned basically everywhere haha

Alejandro12:08:15

@U0P0TMEFJ, makes sense, thank you

👍 1
eskos13:08:59

Still, if you want to at least see what pattern matching could bring you can use either https://git.sr.ht/~technomancy/fnl-match or https://github.com/clojure/core.match 🙂

Ben Sless13:08:00

And #meander

delaguardo13:08:20

and https://github.com/xapix-io/matchete 🙂 there are many more or less active libraries providing pattern-matching abilities

Ben Sless13:08:41

While not a fan of pattern matching, it has its uses. Wrangling HTML, for example

grav14:08:53

In the https://clojure.org/guides/deps_and_cli there's an example of using tags: > {:deps {io.github.yourname/time-lib {:git/tag "v0.0.1" :git/sha "4c4a34d"}}} Can't the tag and the sha get out of sync? What will happen in that case?

grav14:08:52

As far as I can gather, tags can be moved: https://stackoverflow.com/a/20076601/202538

Joshua Suskalo14:08:04

Yes, tags can be moved, but this is actually the reason the sha is included in the first place. If we only had the tag then we would have no way of knowing if the tag was moved. With the sha included, if the tag is moved then the build will fail, and you as the developer have the opportunity to then go browse your dependency to determine the most appropriate version to use.

grav14:08:38

Great, so the sha is mandatory, right?

Joshua Suskalo14:08:59

Yes. Adding the tag however allows you to use a short sha instead of the full one.

grav14:08:17

Ah, nice! Thanks for the explanation 🙂

delaguardo14:08:45

https://clojure.org/reference/deps_and_cli#_git here is a full reference for those options

👍 1
Wanja Hentze14:08:46

The fact that it goes and checks the tag against the sha on every single build is actually annoying because it means you can never build offline: https://clojure.atlassian.net/browse/TDEPS-223

💡 1
Wanja Hentze14:08:25

if that's a concern to you at all, I suggest using full the sha and no tag instead

Alex Miller (Clojure team)14:08:19

tag = semantic meaning for humans sha = specific commit for comparison

Alex Miller (Clojure team)14:08:36

tag + short sha gives you semantics plus verification against a moved tag

Alex Miller (Clojure team)14:08:31

if tag and short sha don't point to the same peeled commit, you'll get an error

Wanja Hentze14:08:38

I believe the same even happens with tag + full sha

Nom Nom Mousse16:08:08

Why doesn't this work?

(import jdk.jshell.JShell)
(.create JShell)
Execution error (IllegalArgumentException) at user/eval49408 (form-init14822045749768224231.clj:1).
No matching field found: create for class java.lang.Class

p-himik16:08:00

Static methods are called differently: (JShell/create).

🙏 1
jjttjj17:08:23

sanity check:

(take 10
  (eduction
    (map println)
    (range 100000)))
;; returns (nil nil nil nil nil nil nil nil nil nil)
;; but nothing is printed
this has to be something weird with my dev tooling setup right? It should be printing? edit: ok yeah it was

enn17:08:11

I think so. It prints for me.

ghadi17:08:07

what's your dev tooling?

jjttjj17:08:08

just inf-clojure with a custom print fn in the repl that basically tries to prevent massive output, turns out to be related to that but not sure how yet

dpsutton17:08:48

can you post your clojure.main/repl invocation here?

dpsutton17:08:12

in inf-clojure I’m seeing:

database-test=> (take 10
                      (eduction
                        (map println)
                        (range 100)))
0
1
2
3
4
...
31
32
(nil nil ... nil)

jjttjj17:08:19

(clojure.main/repl
  :print (fn [x]
           (binding [*print-length* 100
                     *print-level*  20]
             (if (> (count (pr-str x)) 3e6)
               (log/warn "<string too big>")
               (pprint/pprint x)))))

jjttjj17:08:38

yeah it's definitely that if statement that's causing it, a little confused as to why

dpsutton17:08:07

yeah i get the same with your repl. You should add :read clojure.core.server/repl-read so you can quit it with :repl/quit

jjttjj17:08:47

it works fine for me to just create a new one, but thanks for the tip that's probably better

hiredman17:08:03

it is because of the call to pr-str

dpsutton17:08:15

I’m using

(clojure.main/repl
 :print (fn [x]
          (tap> [:print-called x])
          (try
            (if (> (count (pr-str x)) 10)
              (log/warn "<string too big>")
              (pprint/pprint x))
            (catch Exception e (tap> "caught an exception") (tap> e))))
  :read   server/repl-read
  :prompt (fn [] (printf "sub %s=> " (str *ns*))))
with the :read there i’m able to send :repl/quit and change things in the print function to help diagnose.

hiredman17:08:40

pr-str is realizing the eduction inside a binding of *out*

dpsutton17:08:17

ah good catch @U0NCTKEV8. that has with-out-str in it.

hiredman17:08:34

user=> (pr-str
 (take 10
       (eduction
        (map println)
        (range 100000))))
"(0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\nnil nil nil nil nil nil nil nil nil nil)"
user=>

Ben Lieberman17:08:51

interjection but how do I customize my REPL like that? Is there a walkthrough on it somewhere?

hiredman17:08:19

then because of the call to take, the eduction is wrapped in a lazy seq, which caches the results of the compuation, so the second time through with pprint you just get a seq of nils

dpsutton17:08:12

if you are using nrepl i think you are out of luck. If you are using a regular repl then the forms i’ve posted here will work fine for you. the server alias is clojure.core.server Here’s my standard repl:

(clojure.main/repl
 :prompt (fn [] (printf "%s=> " (peek (str/split (str *ns*) #"\."))))
 :eval (fn [f] (binding [clojure.test/*test-out* *out*] (eval f)))
 :read server/repl-read
 :print fipp/pprint)

Ben Lieberman17:08:06

regular REPL as in started with clj or clojure and not through Cider or the like

jjttjj17:08:08

> pr-str is realizing the eduction inside a binding of out aha, thanks!

jjttjj17:08:20

> regular REPL as in started with clj or clojure and not through Cider or the like yeah then you can just call clojure.main/repl with the args described here: https://clojuredocs.org/clojure.main/repl

dpsutton17:08:59

@U03QBKTVA0N yeah. CIDER connects through nrepl which can’t work with subrepls like this. If you try it you’ll see if asks for input from the minibuffer (i believe, been a while)

Ben Lieberman17:08:17

cool, well I'll explore my options. thanks! (excuse the off-topic noise) @U11BV7MTK @U064UGEUQ

jpmonettas17:08:01

I'm running clj -Sdeps '{:deps {somedep/somedep {:mvn/version "2.3.128"}}}' and it is complaining with Error building classpath. Could not find artifact com.github.Dansoftowner:jSystemThemeDetector:jar:3.8 in central () . Looking at the dependency jar pom it has repositories :

<repositories>
    <repository>
      <id>clojars</id>
      <url></url>
    </repository>
    <repository>
      <id>jitpack</id>
      <url></url>
    </repository>
  </repositories>
and that failing dependency is from the jitpack one. Any ideas why cli deps maybe only checking clojars and central and skipping the others?

hiredman18:08:31

searching for artifacts doesn't use repository settings from dependencies, so you'll need to configure any additional repositorie

jpmonettas18:08:04

> you'll need to configure any additional repositorie you mean that all users of the library will have to add it to their setup?

hiredman18:08:31

yes, same is true of any one using maven

jpmonettas18:08:06

damn, I guess I'll just deploy that jar on clojars under my group :man-shrugging:

gleenn18:08:28

Can someone explain how a PersistentArrayMap would sometimes not implement iFn? I got this stack trace very rarely in a production environment running JDK11 and Clojure 1.8:

java.lang.ClassCastException: class clojure.lang.PersistentArrayMap cannot be cast to class clojure.lang.IFn (clojure.lang.PersistentArrayMap is in unnamed module of loader Something @344d449f; clojure.lang.IFn is in unnamed module of loader Something @23b521ad)
    at clojure.core$comp$fn__4730.invoke(core.clj:2461)
    at clojure.core$filter$fn__4815.invoke(core.clj:2713)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.RT.seq(RT.java:521)
    at clojure.core$seq__4360.invokeStatic(core.clj:137)
    at clojure.core$seq__4360.invoke(core.clj:137)
    at clojure.core$filter$fn__4815.invoke(core.clj:2702)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.RT.seq(RT.java:521)
    at clojure.lang.RT.nthFrom(RT.java:938)
    at clojure.lang.RT.nth(RT.java:897)
    at clojure.core$distinct$step__5304$fn__5305$fn__5307.invoke(core.clj:4848)
    at clojure.core$distinct$step__5304$fn__5305.invoke(core.clj:4848)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.RT.seq(RT.java:521)
    at clojure.core$seq__4360.invokeStatic(core.clj:137)
    at clojure.core$seq__4360.invoke(core.clj:137)
    at clojure.core$reduce1.invokeStatic(core.clj:920)
    at clojure.core$reduce1.invoke(core.clj:912)
    at clojure.core$set.invokeStatic(core.clj:3986)
    at clojure.core$set.invoke(core.clj:3977)

hiredman18:08:39

you can see the classes are from different "module loaders"

gleenn18:08:23

but why would that only happen sometimes?

Alex Miller (Clojure team)18:08:53

in other words, it implements IFn, just not the particular one that's being asked about. depends on your classloader hierarchy / environment

hiredman18:08:05

dunno, what is your code doing?

gleenn18:08:37

the code is doing a filter over a map, some very simple code. We have some custom classloader stuff so I guess I need to go chase down why it's loading things in the wrong order or whatever. Thank you both very much!

dpsutton18:08:45

Could this be having two Clojures loaded on two classloaders?

hiredman18:08:57

it is

👍 1
RAJKUMAR19:08:57

I'm new to clojure

ghadi19:08:15

Hi, welcome! may I suggest posting in the #beginners channel?

seancorfield20:08:07

In the #beginners channel, people have opted in to help folks who are new to Clojure in depth. Questions in this channel tend to get answers that assume a fair bit of Clojure knowledge -- so you're going to get better help in #beginners

RAJKUMAR20:08:19

Thanks guys!!!

teodorlu09:08:50

Welcome to Clojure, @U02PR896TMG!

❤️ 1