Fork me on GitHub
#beginners
<
2017-02-14
>
sonelliot01:02:21

Hi everyone. I am looking for some advice on an interop problem I have stumbled into. I am working with a Java API that requires me to extend an existing abstract base-class, add some fields and an annotation. What would be the best way to accomplish this with Clojure? Is gen-class the way to go?

alexmiller01:02:48

@mss given "will then be passed to a method that expects a java.lang.Class”, then you need to use something that creates a (presumably known) class - options are deftype, defrecord, and gen-class

alexmiller01:02:24

if your only constraint is to implement an interface, then deftype might be the easiest:

alexmiller01:02:48

(deftype Foo []
  my.interface
  (interface-method [this] (my-func)))

alexmiller01:02:57

then the class is just Foo

alexmiller02:02:14

(qualified by your current namespace if you need the fully-qualified class)

mss02:02:21

thanks for the response, that makes sense. how does deftype work under the hood? worried about leaking memory from successive calls to e.g. make-an-instance above (except deftype instead of defrecord) to make the body of the interface method dynamic

alexmiller02:02:33

it creates a class

alexmiller02:02:39

if you’re just calling it dynamically (not AOTing code that uses it), it should just be creating classes and registering them in the classloader

mss02:02:19

and over time none of those class declarations would be deallocated, correct? sorry my understanding of jvm memory lifecycle is a little fuzzy

alexmiller02:02:38

depends on stuff :)

alexmiller02:02:31

class unloading is tricky. seems easy to test.

mss02:02:27

great, glad that class unloading is a thing. will look into it more. thanks so much for the help

alexmiller02:02:07

it has evolved a lot in the JVM over the years and Clojure has its own DynamicClassLoader with its own class cache, but it should let that happen

drewverlee02:02:49

I’m reading through Clojure Applied (which is awesome) and i was hoping someone could help me wrap around why the author suggests using a protocols for the core functions of an API. They state.... > Most APIs have this pattern—a handful of key base functions and a larger set of functions provided for ease of use. Protocols are a good way to capture the core set of functions so that multiple implementations can extend that protocol. The derived functions should be provided in the API namespace and layered over the protocol. TheAPI functions then work for any entity that extends the protocol. Is the benfiet of using a protocol that we make the functions needed to implement the API explicit in the protocol. So if a differently shaped set of data wants to implement the API we have documented the core function signatures and some docs. This also seems to imply we should keep our core set of data for the api inside a record, as protocols work on different classes.

tbaldridge02:02:37

Yes, protocols are often used to present an abstract interface. For example, ClojureScript uses protocols to represent the interfaces of data: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L516-L524

drewverlee02:02:30

So it seems the general wisdom is to have abstract interfaces (protocals, records) at your core, but to accept more general data (maps) at your edges.

foamdino07:02:26

I'm confused by this (right at the start of clojure koans) - "What could be equivalent to nothing?" (= nil)

foamdino07:02:38

my first thought/instinct is false

foamdino07:02:45

(= false nil)

foamdino07:02:52

but that is incorrect

foamdino07:02:10

any place I should be looking (without spoiling the answer)?

foamdino07:02:57

obviously this passes (= nil nil) but I'm assuming that isn't 'correct'

mokka07:02:04

i think you can use everything what returns nil, like https://clojuredocs.org/clojure.core/first (first []) ?

mokka07:02:09

but staying in basics like (= nil nil) should be good at start

foamdino07:02:36

hmm - hadn't considered that - thanks for the tip

jeffh-fp13:02:06

(defn sleep-print-update
  [sleep-time thread-name update-fn]
  (fn [state]
    (Thread/sleep sleep-time)
    (println (str thread-name ": " state))
    (update-fn state)))
(def counter (ref 0))
(future (dosync (commute counter (sleep-print-update 100 "Thread A" inc))))
(future (dosync (commute counter (sleep-print-update 150 "Thread B" inc))))
Here’s a timeline of what prints:
Thread A: 0 | 100ms
Thread B: 0 | 150ms
Thread A: 0 | 200ms
Thread B: 1 | 300ms
from Chapter 10 of Clojure for the Brave and True I'm confused why println is called twice (per thread) ... commute shouldn't be retrying the "transaction"

andfadeev13:02:15

commute, on the other hand, behaves like this at commit time:

Reach outside the transaction and read the ref’s current state.
Run the commute function again using the current state.
Commit the result.

andfadeev13:02:27

the commute fn runs in future, the thread sleeps and at the end when state is changing it derefs the state and runs commute fn again

andfadeev13:02:01

it sleeps again and println second time

andfadeev13:02:52

@counter 
=> 2

jeffh-fp14:02:24

I guess I'm just not clear how each future ends up calling sleep-print-update twice

jeffh-fp14:02:36

I thought commute does not retry

andfadeev14:02:30

at commit time commute runs again with current value of state

andfadeev14:02:35

it's not a retry because nothing is compared

jeffh-fp14:02:12

from http://stackoverflow.com/questions/29592681/why-is-the-commute-function-called-twice-when-changing-a-ref-in-clojure it sounds like commute is actually running twice (similar to your explanation above)

jeffh-fp14:02:49

ok well that's confusing 🙂

doddenino17:02:42

Why am I getting two different results when I eval and debug the same function in cider? 😅

not-raspberry17:02:53

Stdout vs return value?

doddenino17:02:13

I get an exception with eval, everything runs fine with debug...

not-raspberry17:02:16

I don't use cider much, paste the code and the results, maybe somebody will help.

lepistane17:02:13

What do you use instead of cider?

doddenino18:02:40

@not-raspberry the code is a little messy, I'll find the solution on my own 😕 Thanks! 🙂

not-raspberry18:02:57

@lepistane fireplace, mostly 🙂