This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-07
Channels
- # babashka (30)
- # beginners (49)
- # calva (22)
- # cider (9)
- # clara (2)
- # clj-commons (1)
- # cljdoc (1)
- # clojars (7)
- # clojure (153)
- # clojure-australia (2)
- # clojure-europe (45)
- # clojure-italy (3)
- # clojure-losangeles (1)
- # clojure-nl (17)
- # clojure-portugal (3)
- # clojure-uk (6)
- # clojurescript (21)
- # conjure (4)
- # copenhagen-clojurians (5)
- # cryogen (3)
- # cursive (19)
- # datahike (14)
- # datascript (4)
- # datomic (9)
- # events (5)
- # fulcro (23)
- # graalvm (1)
- # gratitude (4)
- # helix (2)
- # holy-lambda (5)
- # improve-getting-started (2)
- # jobs (10)
- # kaocha (1)
- # leiningen (1)
- # liquid (8)
- # membrane (81)
- # off-topic (88)
- # polylith (29)
- # quil (1)
- # reitit (2)
- # remote-jobs (8)
- # reveal (8)
- # sci (1)
- # shadow-cljs (14)
- # specter (4)
- # sql (5)
- # tools-build (11)
- # tools-deps (5)
Can someone help me understand the documentation for = ? https://clojuredocs.org/search?q=%3D it says numbers are compared in a type independent way. However, experimentation shows numbers are compute type-DEPENDENT. (= 1 1.0) returns false. I don't understand the claim in the documentation.
~ % clj
Clojure 1.10.3
user=> (= 1.0 (long 1.0) (bigdec 1.0))
false
user=> (== 1.0 (long 1.0) (bigdec 1.0))
true
is an example of the difference (== 1.0 (float 1.0) (bigdec 1.0))
is probably more indicative of the difference since the 1.0
literal will be a long from the reader
yes, but the important thing is that = behaves opposite the documentation.
thus the memoize function won't confuse (f 1)
with (f 1.0)
because hash equality if based on =, not ==
> Can someone help me understand the documentation for = ? https://clojure.org/guides/equality
I suspect the phrase "type-independent way" might be in comparison to how Java does it. Java equality between short and int and long is always "not equal" result. Clojure gives equality between any two integral types that are numerically equal.
Agreed that it is easy to misconstrue that sentence. Clojure built-in docs are infamously terse.
Why do you say that, @U0CMVHBL2? (Not contesting it, just curious 🙂 )
Sorry, weird thing to say. Multiple Clojure core maintainers have expressed a strong preference for, and justification of, brief documentation
I don’t mind brief documentation as such, but OTOH, there are many times that I would have been lost without Clojuredocs. (Thank you, Clojuredocs!) And hopefully, ambiguity can be avoided while keeping the docs short, although I gather how these two aspects can challenge each other at times.
@U0CMVHBL2 I see you’re a clojuredocs contributor, so thank you! 🙏
is it a misprint/typo in the documentation? Or am I just misunderstanding what it is saying?
I asked this about docstring for =
: https://ask.clojure.org/index.php/10992/docstring-for-could-be-made-less-ambiguous-w-r-t-numbers
Thanks. by the way, for my application the current behavior is exactly what I want, despite the documentation. So I'm please, just confused about the docstring.
I want to make sure that if using memoize
(or friends), that a call to the function with 1
is not mistaken for a call to the function with 1.0
.
memoize will be confused
{1 1
1.0 1.0}
inside it is using a map to store arg -> res relation and 1 and 1.0 are not the sameso it WONT be confused. unless I'm confused.
since 1 is different (not = ) to 1.0, they will be memoized separately. right?
did I misunderstand something here?
(defn foo [n]
(prn n)
n)
(def mfoo (memoize foo))
(mfoo 1) ;; print 1
(mfoo 1.0) ;; print 1.0
(mfoo (int 1)) ;; no print
what does (int 1) do?
so the docstring for =
is missing some explanation what is “type-independent manner” is
(= (long 1) (short 1))
this returns true
it cast Long to Integer in clojure every every number is either Long or Double.
(type 1) ;; => java.lang.Long
(type (int 1)) ;; => java.lang.Integer
is that a clojure thing, or does that mean my CPU represents short and long both as 64 bit integers?
no, short is short
but 1
is Long
1 as you have it in written code
OK, so = thinks a (long 1) is equal to (short 1), this means that memoize will confuse them.
so perhaps = does in fact do SOME type-independent magic ???
yes, some. and explanation what sort of “some” is missing in the docstring
👍:skin-tone-2:
clojure-rte.rte-core> (defn foo [n] (prn n (type n)) n) #'clojure-rte.rte-core/foo clojure-rte.rte-core> (def mfoo (memoize foo)) #'clojure-rte.rte-core/mfoo clojure-rte.rte-core> (mfoo 1) 1 java.lang.Long 1 clojure-rte.rte-core> (mfoo 1) 1 clojure-rte.rte-core> (mfoo (short 1)) 1 clojure-rte.rte-core> (mfoo (long 1)) 1 clojure-rte.rte-core> (def mfoo (memoize foo)) #'clojure-rte.rte-core/mfoo clojure-rte.rte-core> (mfoo (short 1)) 1 java.lang.Short 1 clojure-rte.rte-core> (mfoo (long 1)) 1 clojure-rte.rte-core>
from that page - https://clojure.org/guides/equality
Clojure's = is true when called with two immutable scalar values, if:
Both arguments are numbers in the same 'category', and numerically the same, where category is one of:
* integer or ratio
* floating point (float or double)
* BigDecimal.
so short, int, long falls into category integer but I’m surprised to see ratio and integer in the same one
(= (clojure.lang.Ratio. (biginteger 1) (biginteger 1)) 1) ;; => false
ahh and of course I forgot that (= [1] (list 1))
so I suppose memoize will conflate these as well.
An even weirder example:
user=> (= 1/1 (clojure.lang.Ratio. (biginteger 1) (biginteger 1)))
false
user=> (= 1/2 (clojure.lang.Ratio. (biginteger 1) (biginteger 2)))
true
yeah %)
this is not the same but caught me few times )
(identical? (Boolean. true) (Boolean. true)) ;; => false
What is the purpose of wrap-fn
in core.cache
? Could I not have just wrapped value-fn
myself?
At least in the case of core.cache.wrapped/lookup-or-miss
the wrap-fn
is provided the value of e
as well. (the default wrap-fn is #(%1 %2)
) with %1 being value-fn and %2 being e
)
Seems to be the same with core.cache/through
:
(defn through
"The basic hit/miss logic for the cache system. Expects a wrap function and
value function. The wrap function takes the value function and the item in question
and is expected to run the value function with the item whenever a cache
miss occurs. The intent is to hide any cache-specific cells from leaking
into the cache logic itelf."
([cache item] (through default-wrapper-fn identity cache item))
([value-fn cache item] (through default-wrapper-fn value-fn cache item))
([wrap-fn value-fn cache item]
(if (clojure.core.cache/has? cache item)
(clojure.core.cache/hit cache item)
(clojure.core.cache/miss cache item (wrap-fn #(value-fn %) item)))))
yes I’ve seen that too…
I still don’t get the design choice here though, as value-fn
also receives the value of e
; so I could’ve just wrapped it myself.
Yeah, I recently stumbled on this because I gave lookup-or-miss a map and the default wrap-fn isn't documented so I got some pretty interesting behaviour.
But since it's in there, there probably is a use-case for having separate value- and wrap-fns?
Which is exactly why I’m asking 🙂
is there a way to prevent gen-class
creating implementations for default methods of an interface ?
I am using clojure.core.memoize
and clojure.core.cache
as following:
(defn gc-friendly-memoize
[g]
(m/memoizer g (c/soft-cache-factory {})))
Is there a way to ask the caching mechanism to give me any debug feedback? I'm not 100% sure what kind of feedback I'd like. But information such as how many values are cached, and how many get released during GC for example would be useful.I think you could look at the soft cache directly and count its items or use SoftReference.get
on each value to see how many have been garbage collected
@gregg.walrod #off-topic is a good channel to chat about anything.
not sure how up to date this all is now but https://clojure.org/community/training has some resources
(and if it's not up to date, issues/prs to https://github.com/clojure/clojure-site/issues welcome)
Any recommendations for libraries that support retries with backoff at a minimum, or full on service orchestration? We're using AWS Step Functions right now and they're getting rather expensive.
Back in the day we used https://github.com/joegallo/robert-bruce which worked pretty well for retries with backoff but I'm wondering if there is something more recent that's better.
Thanks Darin, that looks like a really good option.
I do some variation of
(trampoline
((fn f [sleep]
(try
(do-something)
(catch Throwable t
(if (> sleep (* 60 1000))
(throw t)
(do
(Thread/sleep (* (rand) sleep))
#(f (bit-shift-left sleep 1)))))))
1))
which is basically robert.bruce. safely looks neat. https://www.youtube.com/watch?v=m64SWl9bfvk may be an interesting some what related talk (circuit breakers, retries, load balancing, etc)Thanks Kevin, I figured you'd have some good input. 🙂
I have an issue with loading data from a folder in my resources
, the structure is as shown below. In my code I want to automatically discover all files present in the data
folder with (.listFiles (file (resource "data")))
with file
and resource
being referred from http://clojure.java.io. This works in the REPL, but as soon as I pack my code into a library, install it in the local repo and call it from a different application, I get `Execution error (IllegalArgumentException) at libraryname.core/load-all (core.clj:14).
Not a file: jar:file:/Users/zoeller/.m2/repository/path/to/jar/0.1.0/libraryname-0.1.0.jar!/data`
When I unpack the JAR, the folder structure is definitely there as I would have expected it.
It should be an URL
, but file
should pass it on to the Coercions protocol for URL, should’t it?
URL is irrelevant. URL can encode lots of different things (files, zips, http, ftp, sftp)
Ah, I get it. so it’s not a file://
protocol, and thus fails
It’s definitely not how Java expects you to use resources. I don’t immediately know how it might break things, but I suspect that it could break somehow.
New resources will be added to the lib from time to time, and I wanted to avoid creating a registry.
you can walk a zip/jar file and check the entries that match a certain directory - why not?
e.g. if you have 10 jars on your classpath, how do you know which one to walk? Just walk all of them?
(io/resource "the-directory")
was my try for the Jar, with aforementioned outcome 😉
user=> (require '[ :as io])
nil
user=> (io/resource "clojure")
#object[java.net.URL 0x5226e402 "jar:file:/Users/borkdude/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar!/clojure"]
(io/resource "foo/bar.edn")
and (io/resource "foo/baz.edn")
- afaik bar.edn
and baz.edn
are allowed to be in separate jars.
Yeah, you definitely wanna fully namespace this sucker. But also know that you’re doing something that Java doesn’t really expect.
I'm doing a similar thing in clj-kondo where I walk through all jar entries and copy all clj-kondo.exports
entries
So here’s what Spring does: https://github.com/spring-projects/spring-framework/blob/ccb080f948df45c36b21135759795cd7e572b813/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java#L494-L523
so basically walk through all entries every time, dispatching on the type of the entry
That’s the other thing: How do you switch between dev and prod? You’d have to dispatch on the resource type.
Yeah that’s probably enough: 1. Use a fully-qualified path, 2. Dispatch on the protocol of the URL
Thanks, you two, that was pointing me into the right direction. Now getting a seq of paths with this code (which is not pretty). Getting to late here today, but I think I will get this somewhere tomorrow.
Final result which is working now: https://github.com/lambdaschmiede/freitag/blob/master/src/freitag/core.clj
@U0N9SJHCH: That looks quite good, I’ve done this sort of thing in the past too.
You might be ok here for your use case, but a word of warning for others. In my experience you need to be careful when using the java FileSystems API to read jars/zips.
In particular you’ll find that internally it maintains a registry/cache of open file systems — which essentially means you can’t open and close the same zip file on multiple threads without extra coordination over the shared file system resource.
I have some code in of our projects which wraps this properly to support safe concurrent access into the same zip file from multiple threads.
For your code it looks also like you have a minor bug; which is that you never seem to close the filesystem once you’re done with it.
I’d imagine this will mean in a REPL if you execute path-from-fs
twice on the same zip/jar it will die with an exception the second time.
To fix this you’ll need to put the file-system inside a with-open
and make sure you eagerly consume it.
@U06HHF230 thanks for the heads up! You are right, I missed closing the Filesystem (which also meant the code can only run once).
I have some code in one of our projects which I should probably open-source as a library that properly wraps the FileSystems
calls so it supports safe concurrent access to the same zip file across threads.
Will add a with-open
in the afternoon. Multiple threads shouldn’t be an issue, as the load function is only called once for initializations, but I’ll look into that also and maybe add some words of warning to the Readme
yeah multiple threads shouldn’t be a problem here — though you may want to consider moving the side-effect so it only occurs when you call query, and caches it at runtime…. e.g. perhaps (def vacations (delay (load-all))
Just so any problems reading occur at the time of the first query, rather than initialisation time
Delaying the read would be also interesting for the library, if you do not need all of the resources, we will add more files over time and it would be more lightweight not to dump everything into memory in the beginning
I'm using the aleph package (version 0.4.7-alpha7). According to the aleph project.clj, there's a 0.4.7-alpha8, but I haven't upgraded to that yet because when I try to I get:
Error building classpath. Could not find artifact aleph:aleph:jar:0.4.7-alpha8 in central (https://repo1.maven.org/maven2/)
So for now, I guess I'm using alpha7. Aleph uses manifold - in its case, version 0.1.9-alpha3. Of course, manifold is now up to version 0.1.9, so I've overrode aleph's version in my local deps.edn, by using
deps: {
...
manifold/manifold {:mvn/version "0.1.9"}
... }
So far, so good. Everything seems to load. And from a versioning standpoint, all should be well (-ish). However, in aleph, I get to the point netty.clj:797 where I make a call to manifold:
(manifold.executor/thread-factory
#(str prefix "-" (swap! num-threads inc))
(deliver (promise) nil)
nil
daemon?)
Note that this call has four parameters. Now for the odd part. When I look at the call thread-factory in manifold executor, there is a four-parameter argument defined (executor.clj:35):
(defn ^ThreadFactory thread-factory
([name-generator executor-promise]
(thread-factory name-generator executor-promise nil true))
([name-generator executor-promise stack-size]
(thread-factory name-generator executor-promise stack-size true))
([name-generator executor-promise stack-size daemon?]
(reify ThreadFactory
(newThread [_ runnable]
(let [name (name-generator)
curr-loader (.getClassLoader (class thread-factory))
f #(do
(.set executor-thread-local @executor-promise)
(.run ^Runnable runnable))]
(doto
(if stack-size
(Thread. nil f name stack-size)
(Thread. nil f name))
(.setDaemon daemon?)
(.setContextClassLoader curr-loader)))))))
But when this call is made, I get the following error:
; Execution error (ArityException) at aleph.netty/enumerating-thread-factory (netty.clj:797).
; Wrong number of args (4) passed to: manifold.executor/thread-factory
Anyone got an idea about what's going on? I'd say it's a versioning issue, but aleph out of the box is using an earlier version of manifold, and manifold has released a four-argument version of this function.
in a repl you could type (source manifold.executor/thread-factory)
and see what source is actually being used
I would double check your full dependency graph, if some library you are using was aot'ed with an older version you could be getting that instead
% clj -Sdeps '{:deps {manifold/manifold {:mvn/version "0.1.9"}}}' -M -e "((requiring-resolve 'manifold.executor/thread-factory) str (promise) 1000 true)"
#object[manifold.executor$thread_factory$reify__481 0x6d2dc9d2 "[email protected]"]
%
suggests it is something with your dependenciessomething like
(->> (requiring-resolve 'manifold.executor/thread-factory) deref class .getDeclaredMethods (filter #(= (name 'invoke) (.getName %))) (map #(count (.getParameterTypes %))))
will tell you the arities the fn object acceptsand if the arities it accepts don't match what you see in the source, that means the fn object didn't come from the source you are looking at
I've checked the versions and, according to clj -Stree |grep manifold
, I'm getting:
X manifold/manifold 0.1.9 :use-top
manifold/manifold 0.1.9
X manifold/manifold 0.1.9-alpha3 :use-top
X manifold/manifold 0.1.8 :use-top
The ones to the right, headed with the X, are dependencies in the libraries. According to the X prepending the tree node and the :use-top annotation, these should be replaced with manifold/manifold 0.1.9 version that's getting loaded according to its leftmost position. However, the top dependence says to ignore 0.1.9, which seems odd to me.
More troubling, when I run your last snippet, it's telling me there's only diadic and triadic versions of this function. So I guess my question is "How do I use my top-level deps.edn to tell the build system to override all local dependencies and use manifold/manifold 0.1.9?"
I don't have an aot'ed manifold to test on but something like
(-> (requiring-resolve 'manifold.executor/thread-factory) deref class (.getName) (.replaceAll "\\." "/") (str ".class") ((requiring-resolve ')))
will return nil if the function was created from source, otherwise a url pointing to where the class was loaded to on disk (likely in a jar file)my guess is you'll find a dependency badly packaged, and including an aot'ed older manifold version
in which case you are kind of out of luck, if a dependency is including stuff from manifold in their artifact you cannot exclude it
if you find the jar file for in your m2 and use jar -tf you will likely get a listing of files that includes classes from manifold
re "How do I use my top-level deps.edn to tell the build system to override all local dependencies and use manifold/manifold 0.1.9?" - top deps override all (that's what you're seeing above), so I think you are getting 0.1.9.
(System/getProperty "java.class.path")
would tell you your classpath at runtime and you should be able to support the actual path to the jar there
an uberjar, or aot'ed, and including source or classfiles from their transitive deps
true, if you've got a dependency jar folded into something else, that would be a problem
you might be able to load the resource to the manifold class or source clj and tell which jar it's in
whatever there are getting in the repl doesn't match the source for 0.1.9 (looking at the arities of the defined invoke methods)
aleph is using manifold 0.1.9-alpha3. aleph also uses byte-streams 0.2.5-alpha2, which uses manifold 0.1.8. The current version 0.2.5 of byte-streams of course uses version 0.1.9 of manifold. If everything were up-to-date in the version of aleph available from maven, life would be good. But that's just me complaining. In any case, the manifold versions are not in those jar files. Why would tools not use the overriding manifold 0.1.9 from my top-level deps.edn? Or am I misunderstanding how :extra-deps works?
It doesn' matter. I cleared them out manually (can't beat an rm -rf) and the pom files for the aleph and byte-streams keep dragging back the old versions. Can I manually edit the .pom files to point to the new version?
if the issue is what I think it is then no you cannot do anything about it, but it may not be what I think it is
if the issue is some other jar files (not the manifold jar) are including either source code or generated classes from manifold, then excluding the manifold dependency isn't going to do anything
and if that is the case cleaning won't do anything, it is a problem with how the dependency is packaged
but we don't know for sure if that is the case, you'll need to try and track down where the manifold.executor/thread-factory you have, that doesn't have the right arity, is coming from
which is what
(-> (requiring-resolve 'manifold.executor/thread-factory) deref class (.getName) (.replaceAll "\\." "/") (str ".class") ((requiring-resolve ')))
I mentioned earlier is forinterpreting the result is a little problematic, but if it returns nil that means the class was created by evaluating source, and if it doesn't return nil that means it is likely(but not certain) the class was created by loading a class file from disk
you also just want to look at
(-> (requiring-resolve 'manifold.executor/thread-factory) deref class)
in the repl, if the output is not manifold.executor$thread-factory
then likely something is trying to monkey patch the manifold librarypom files, deps.edn, etc describe dependencies between blobs of stuff (code, resources, etc), and editing poms, exclusions in deps.edn, etc effect that dependency graph