Fork me on GitHub
#beginners
<
2022-06-22
>
yi03:06:15

i need to use java.rmi. java examples generally go like this

package compute;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    <T> T executeTask(Task<T> t) throws RemoteException;
}
what is the way to extend a java interface in Clojure? can i do it with defprotocol?

phronmophobic04:06:26

Are you trying to extend an interface or implement an interface? I'm not sure of the exact syntax, but, implementing an interface is usually done with reify, (see https://clojure.org/reference/java_interop#_implementing_interfaces_and_extending_classes)

(reify compute.Compute
    (executeTask [this t]
      (println "doing stuff")))

yi04:06:01

all the examples i've seen for rmi go by first extending it into another interface plus adding that exception thrown, and only then implementing it. it seems like https://stackoverflow.com/questions/5479694/extending-not-implementing-java-interfaces-in-clojure with gen-interface , but if i'm not adding that throw exception it seems not much useful do to that. So I guess I'll try implementing it directly with reify as you suggest. Thank you!

phronmophobic04:06:15

yea, extending the interface isn't really necessary in Clojure and I'm not sure it would provide any benefit since it's not being checked by Java's static type checker.

phronmophobic04:06:46

I'm not actually sure the checked exceptions even show up in the bytecode

phronmophobic04:06:50

I would generally start with reify , and fallback to defrecord or proxy depending on the use case. That should cover most interop use cases, but it's also not that hard to include a small amount of java if that allows you to avoid awkward interop cases introduced from weird javaisms.

❤️ 1
popeye06:06:01

I have a function call which is calling the function one-by-one... how can we avoid sending arguments to each functions? I do not want those arguments in function 3 4 5 6, I need extra arguments only in function 1 and function 7

Martin Půda06:06:16

Can you post an example code?

lsenjov06:06:25

You can use dynamic vars - it's a global variable that gets rebound in the scope of a thread. It's generally not a good thing to do (it complects code where it's used to where it's bound), but it has its uses. You can't use it if it has any sort of async (the var is still bound to the thread)

lsenjov06:06:56

(Specifically the ^:dynamic tag)

lsenjov06:06:51

Note that this is generally not a good thing to use - if you know the function at the bottom needs it, you should pass it down through the intermediate functions

popeye06:06:55

so you mean passing parameter to each function

lsenjov06:06:14

Doing it with dynamic vars means you have slightly less boilerplate on the way down, but can introduce a lot of complexity

rolt06:06:19

you can just modify or wrap those inner function to accept the same number of argument (or even any number of argument (fn [a b c & _] (f a b c)) )

lsenjov06:06:41

Since it's not immediately obvious you're needing the var to be bound

popeye06:06:04

@U01RL1YV4P7

(defn transformation [arg1 arg2 arg3 arg5 arg6 ]
  )

(defn function-5 [ arg1 arg2 arg3]
  ;; Need arg5 args6
  (transformation arg1 arg2 arg3 arg5 arg6 )

  
  )

(defn function-4 [ arg1 arg2 arg3]
  (function-5  arg1 arg2 arg3)
  )

(defn function-3 [ arg1 arg2 arg3]
  (function-4  arg1 arg2 arg3)
  )

(defn function-2 [ arg1 arg2 arg3]
  (function-3  arg1 arg2 arg3)
  )

(defn function-1 [ arg1 arg2 arg3]
   (function-2  arg1 arg2 arg3)
  )

rolt06:06:28

(defn f1 [{:keys [a b]}])
(defn f2 [{:keys [a b c]}])

rolt06:06:53

if you struture you functions like that you can pass the whole hash map

lsenjov06:06:03

Ah yes, option maps would work well

popeye06:06:36

@U02F0C62TC1 thanks for your response, I have hash-map or atom as my initial plan which I had that in my mind. But wanted to find better solution if any 🙂 if we do not have any other option I will go with hash-map

popeye06:06:02

can' t we do this using partial function ?

lsenjov06:06:56

You can, but it means every level you're doing multi-arity boilerplate

lsenjov06:06:25

Options maps are excellent because you can pass down all your arities in a single item, and in your destructuring you can refer to the keys you care about at that level

rolt06:06:47

i dont understand the intention in your code sample so it's a bit hard

popeye06:06:36

@U24QP2D4J, If I use it as dynamic variable, when the value of that will be garbage collected ?

rolt06:06:10

if you wrap the call in binding, it should be gc-ed at the end

popeye06:06:13

what if I do it using atom, when it will be cleared? only calling reset! function ?

rolt06:06:14

when you modify the value the old one is cleared

lsenjov06:06:24

Then you introduce state that's not thread-local

lsenjov06:06:30

And things go off the rails

rolt06:06:06

oh I think I get the sample, yes the usual clojure way is to pass those additional args to each functions, could be an option map. In some rare cases, or in tests it's sometimes easier to use dynamic vars. And it's okay to introduce a global atom state sometimes, but beware of concurrency

lsenjov06:06:02

I mean, in general don't introduce a global state atom unless it's actually global state

lsenjov06:06:43

Introducing a global state atom to run in a scoped state is asking for trouble

Hermann09:06:47

What about

(defn f1 [a1 a2 a3] [a1 a2 a3])
(defn f2 [a1 _  a3] [a1 a3])
(defn f3 [_  _  a3] a3)
(defn f4 [a1 & _] a1)

((juxt f1 f2 f3 f4) 1 2 3)
> ([1 2 3] [1 3] 3 1)

popeye06:06:36

these are function inside a function

sheluchin13:06:27

Is there any clever way to diff two values between different executions for debugging? I'm resorting to a lot of copy/paste manual diffing... wonder if there's a better way.

delaguardo13:06:18

there are few projects this library as an example - https://github.com/friemen/diffit in the README you can find links to others

sheluchin13:06:41

I wasn't aware of that one, but I do use https://github.com/lambdaisland/deep-diff2. The issue isn't so much in diffing two structures, it's moreso about storing the value between two different execution runs. My workflow is something like: start with working copy -> make a seemingly benign change -> notice bug -> compare values between working copy and bug which resulted from "benign" change. The copy/paste required for comparison is just very tedious at times. Is there a better way?

rolt13:06:32

you could write a test and run it on save

sheluchin13:06:28

@U02F0C62TC1 if I'm debugging I don't necessarily know what to test.

rolt14:06:48

so ideally you'd want a tool that instrument the return values of functions and store multiple versions ? I'm not aware of such a tool but i might exist. spyscope or postmortem could help a bit maybe

sheluchin14:06:47

Yes, a tap-diff tool or something like that. Or, alternatively, a better approach altogether. Spyscope looks interesting. Will play around with that one.

rolt14:06:49

I feel like it already exists and I don't know it 😅

jumar16:06:45

Perhaps the new flowstorm debugger could also be a good fit for such a feature

lepistane14:06:19

how do i (apply and '(true false false)) ? right now i am getting Can't take value of a macro: #'clojure.core/and but i don't understand why that's happening? reason is i have variable list of conditions and i need to use and on it don't know how to do it properly

Ferdinand Beyer15:06:03

Just to answer your original question as well 🙂 This happens because and is not an ordinary function, but a macro, that will expand to an if! This is needed so that it can short-circuit, e.g. (and false (println "Hello")) will not print. This is not possible with ordinary functions. Macros cannot be passed around like other functions, e.g. you cannot pass it to apply. That’s what the error message Can't take value of a macro means.

👍 1
lepistane14:06:57

Thanks! Just read this (little late ) 🙂

👍 1
Alex Miller (Clojure team)14:06:44

(every? true? '(true false false))

lepistane14:06:53

i may get non boolean in that list

Alex Miller (Clojure team)14:06:57

or (every? identity '(true false false))

👍 1
👀 1
🙌 1
😮 1
lepistane14:06:29

little bit ashamed it was so obvious. Thanks a lot !

rolt14:06:42

some instead of every? no ?

👍 1
rolt14:06:21

but every? is and

lepistane14:06:11

That's a very good point. I actually made a mistake because i needed and anyway and my test cases passed. I will correct original thread

rolt15:06:14

but now my reply looks stupid 😅

lepistane17:06:17

i disagree, you actually corrected my question and provided more context to other functions

sheluchin15:06:34

Is there a better approach to this pattern?

;; filter seq of maps and then select a subset of the keys
(transduce
  (comp
    (filter #(odd? (:x %)))
    (map #(select-keys % [:z :y])))
  conj
  (for [i (range 5)] {:x i
                      :y (* i 10)
                      :z (* i 100)})
; => [{:z 100, :y 10} {:z 300, :y 30}]

rolt16:06:54

(keep #(when (odd? (:x %)) (select-keys % [:z :y]))
  (for [i (range 5)] {:x i
                      :y (* i 10)
                      :z (* i 100)}))

rolt16:06:22

but even with the for only if it's part of your question:

(for [i (range 5)
           :when (odd? i)] {:y (* i 10)
                      :z (* i 100)})

sheluchin16:06:26

@U02F0C62TC1 The first one is what I was after. Thanks!

rolt16:06:44

and with transducer:

(sequence
  (comp
    (filter #(odd? (:x %)))
    (map #(select-keys % [:z :y])))
  (for [i (range 5)] {:x i
                      :y (* i 10)
                      :z (* i 100)}))

🙏 1
sheluchin16:06:28

Ah, "application of transducer to an input collection" :pencil:

Ferdinand Beyer16:06:42

“better approach” is super subjective. While I find transducers cool, they are often hard to read and understand, especially when mixed with other constructs, like in your example.

Ferdinand Beyer16:06:01

E.g. you could make it all transducers:

(sequence
  (comp (map #(hash-map :x % :y (* % 10) :z (* % 100)))
        (filter #(odd? (:x %)))
        (map #(select-keys % [:z :y])))
  (range 5))

Ferdinand Beyer16:06:31

Or none:

(->> (range 5)
     (map #(hash-map :x % :y (* % 10) :z (* % 100)))
     (filter #(odd? (:x %)))
     (map #(select-keys % [:z :y])))

Ferdinand Beyer16:06:23

I think transducers are great when you can reuse functionality in places other than collections, like in core.async channels. But in most cases like these here, I would avoid them, as they are harder to understand than using (lazy) sequences. You need to jump between forms to understand what happens. With ->> it reads super nicely: It’s just a pipeline of transformations.

sheluchin16:06:07

Yes, it is somewhat challenging to understand when to use which option. In my case I have an ETL pipeline where there are a many records being transformed and performance at scale is a factor. My understanding is that the transducer option will generally perform better in such circumstances.

Ferdinand Beyer16:06:33

Like always when it comes to performance: Don’t let your intuition fool you. Write clean code, then measure.

❤️ 1
Ferdinand Beyer16:06:56

Performance at scale: Sounds like you will distribute over multiple computers, then this kind of micro-optimisation will probably not make a difference. Plus fast does not help if the code has bugs because it was hard to understand 🙂

sheluchin16:06:59

@U031CHTGX1T > You need to jump between forms to understand what happens. Can you elaborate on this point? You have to "jump between" different expressions in a threaded sequence transformation as well, no? Are you referring to the combination of stateful transducers and ones that process individual elements?

Ferdinand Beyer16:06:23

I meant when you read the code and try to understand it.

Ferdinand Beyer16:06:19

To understand this:

(transduce
  (comp
    (filter #(odd? (:x %)))
    (map #(select-keys % [:z :y])))
  conj
  (for [i (range 5)] {:x i
                      :y (* i 10)
                      :z (* i 100)})
You have to jump into the for to see that it works on range, then jump to the body of for, then up to the transducer, then you have to remember in which order transducers are run when combined with comp. There’s a lot to grasp. Compare this to:
(->> (range 5)
     (map #(hash-map :x % :y (* % 10) :z (* % 100)))
     (filter #(odd? (:x %)))
     (map #(select-keys % [:z :y])))
This is super straight-forward

sheluchin16:06:07

Sometimes the combination of lazy and eager functions trips me up. With a transducer you can be sure that the whole thing is consumed in a non-lazy way, yes?

rolt16:06:39

oh with sequence it was, lazy, for the non lazy transducer way: (into [] (keep ...) coll)

👍 1
gratitude-thank-you 1
sheluchin16:06:39

Or... I guess when you produce a sequence transformation with ->> the next step is deciding how it will be consumed, so the laziness isn't a factor in what you wrote.

Ferdinand Beyer17:06:09

You can always add a doall to the end to ensure everything is eagerly evaluated, or use mapv or into 🤷

gratitude-thank-you 1
Ferdinand Beyer17:06:01

But remember that premature optimisation is the root of all evil.

sheluchin17:06:27

heh, I don't think Donald Knuth meant that the way people use that these days 😛 But yes, your point stands. Although with those "tricks" I have run into memory consumption issues here and there.

Quentin Le Guennec19:06:28

hello, is that function in clojure.core? (f "account" :name) => :account/name

seancorfield19:06:00

Almost: (keyword "account" "name") => :account/name so you'd want (keyword "account" (name k)) for an arbitrary keyword k