clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
matheusashton 2021-01-29T01:33:17.495200Z

Hello! Does prismatic/schema has some type of union types? Can I do an or with 2 type definitions? :thinking_face:

borkdude 2021-01-29T09:45:01.495800Z

it also has either

vemv 2021-01-29T10:13:28.496Z

it's deprecated for some reason (while cond-pre is alpha... ๐ŸŒ€ in practice it works well)

vemv 2021-01-29T01:39:55.495300Z

its or is called cond-pre (odd, I know)

borkdude 2021-01-29T11:23:04.496800Z

I recently asked how to get the matches with core.match from a regex, now I figured it out:

(require '[clojure.core.match :refer [match]])

(defn regex [x]
  (re-matches #"(\w+)->(\w+)" x))

(match "foo->bar"
       ([_ x y] :<< regex) [x y]
       :else [])

;;=> ["foo" "bar"]

1
borkdude 2021-01-29T11:23:47.497200Z

it would be very funky if regexes were also IFns in clojure ;)

๐Ÿ™Œ 1
bronsa 2021-01-29T11:28:51.497800Z

@borkdude not easy in the JVM -- Patterns are final and there's no interface that backs them

bronsa 2021-01-29T11:29:06.498200Z

so making a Pattern that is an IFn is not possible

bronsa 2021-01-29T11:29:30.498600Z

you'd have to use a wrapper class, but then interop becomes indirect

borkdude 2021-01-29T11:29:54.499100Z

I guess it would have worked if IFn was a protocol from the start. Not sure what would have been the perf implication

borkdude 2021-01-29T11:30:36.499900Z

but also, not sure if this is a good idea at all, since it's not clear what implementation for IFn would be the best, re-find, or re-matches

borkdude 2021-01-29T11:31:45.000400Z

Just a funky idea ;)

bronsa 2021-01-29T11:32:04.000600Z

yeah a protocol for IFn (with the normal protocol semantics) would be a big performance penalty

bronsa 2021-01-29T11:32:15.000900Z

not sure if that's still true in the age of indy/condy

stephenmhopper 2021-01-29T12:49:42.002300Z

I have a question regarding Clojure and GC. Suppose I have code like this:

(defn my-fn []
  (let [xs (fn-which-produces-lazy-seq)]
    (->> xs
      (map some-side-effect-fn)
      (doall))
    nil))
Will the entirety of xs be held in memory at least until the function exits?

borkdude 2021-01-29T12:50:38.002700Z

@stephenmhopper with doall yes, with dorun no

borkdude 2021-01-29T12:51:24.003200Z

if you only need to walk the collection with a side effecting fn, you can also use run! or doseq

p-himik 2021-01-29T12:54:13.003500Z

Why, given that the value of doall ends up being unused?

stephenmhopper 2021-01-29T12:55:01.003800Z

Based on the docs for doall and dorun, it looks like doall retains a pointer to the head, but dorun does not?

borkdude 2021-01-29T12:56:48.004Z

yes

p-himik 2021-01-29T12:58:05.004200Z

"Retains a pointer" means just that it returns its argument instead of returning nil. But later that argument is still ignored in your my-fn, so I still don't see how using doall would cause the pointer to that coll being retained.

p-himik 2021-01-29T12:59:12.004400Z

doall is, abridged:

(defn doall [coll]
  (dorun coll)
  coll)

borkdude 2021-01-29T13:00:36.004600Z

The entire collection xs will be realized and kept into memory at one point, which is what I meant. After the doall call it can be GC-ed if it's no longer used

borkdude 2021-01-29T13:00:49.004800Z

which is, in the example, the end of the function

p-himik 2021-01-29T13:02:06.005Z

Just so we're on the same page here - if we replace that last nil in my-fn with (Thread/sleep forever) then with both doall and dorun the xs collection will be GC'ed despite the function never exiting, right?

stephenmhopper 2021-01-29T13:03:11.005200Z

xs is still pointing to the head of the collection though, so wouldnโ€™t that prevent it from being GCโ€™d regardless of whether we use doall or dorun?

stephenmhopper 2021-01-29T13:03:53.005400Z

Or does the Clojure compiler work some kind of magic to release that pointer early?

borkdude 2021-01-29T13:04:29.005600Z

@p-himik yes

๐Ÿ‘ 1
borkdude 2021-01-29T13:05:00.005800Z

@stephenmhopper Clojure has locals clearing. So if a local isn't used in the body anymore, it releases it for GC

borkdude 2021-01-29T13:05:29.006Z

https://clojure.org/reference/compilation#_locals_clearing

๐Ÿ’ฏ 1
p-himik 2021-01-29T13:07:03.006300Z

It also might be true in Java, according to a SO post: > While the object won't be garbage collected if it is still in scope, the JIT compiler might take it out of scope if the variable isn't actually used any further in the code [...] even though when you read the source code the variable still seems to be "in scope."

๐Ÿ‘ 2
stephenmhopper 2021-01-29T13:07:36.006500Z

Thatโ€™s good to know. Thanks @borkdude and @p-himik

2021-01-29T16:00:25.007900Z

Can you elaborate on the performance penalty? I thought it was just another function call & a type dispatch, but I admit Iโ€™ve never looked into the internals here.

2021-01-29T16:57:36.009Z

I've written some code to convert some java objects to clojure data structures, i want to write some tests, whats the best/easiest way to creat java classes/objects in clojure?

lukasz 2021-01-29T17:12:54.011500Z

The most obvious one is to just instantiate these Java objects in your Clojure tests

2021-01-29T17:13:44.011700Z

I need to define what the classes are though, i.e. I want to define a class with the methods getX() and getY()

lukasz 2021-01-29T17:16:13.011900Z

Right - there's a couple of ways to do it - you can add Java files to your project, and let your tooling compile them and make them available in your class path, or you can use the interop and create Clojure namespaces which will produce classes/objects you need, or lastly if you have interfaces to implement you can use something like proxy: https://clojure.org/reference/java_interop#_implementing_interfaces_and_extending_classes or https://clojuredocs.org/clojure.core/gen-class

p-himik 2021-01-29T17:26:14.012100Z

And if you want to avoid AOT hassles, you can use https://github.com/tailrecursion/javastar or https://github.com/ztellman/virgil

p-himik 2021-01-29T17:27:11.012500Z

The latter requires writing Java and there's no immediate way of making it work with a deps.edn-based project - you will have to write a few lines of code. I haven't used the former so can't really comment on it.

bronsa 2021-01-29T17:33:23.012700Z

invoking an IFn is just a checkcast followed by an invokeinterface call

๐Ÿ‘ 1
bronsa 2021-01-29T17:35:20.012900Z

on the other hand, a protocol function invocation is around 20 bytecodes (inline caches, direct invocation paths etc)

bronsa 2021-01-29T17:35:42.013100Z

even in the fast case where it bouls down to an invokeinterface, there's an interface instanceof + goto , which is fast but still slower than not doing it

๐Ÿ‘ 1
bronsa 2021-01-29T17:36:12.013300Z

but the extra unused bytecodes will mean that JVM inlining will be affected

emccue 2021-01-29T17:39:13.013900Z

clojure.java.data if you are just working with library stuff