This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-15
Channels
- # announcements (1)
- # aws (79)
- # babashka (47)
- # beginners (82)
- # calva (65)
- # cider (27)
- # cljdoc (18)
- # cljs-dev (29)
- # clojure (189)
- # clojure-dev (5)
- # clojure-europe (3)
- # clojure-italy (1)
- # clojure-madison (6)
- # clojure-nl (4)
- # clojure-spec (10)
- # clojure-uk (41)
- # clojured (3)
- # clojurescript (5)
- # clojurex (17)
- # cursive (30)
- # data-science (7)
- # datomic (17)
- # emacs (3)
- # events (6)
- # fulcro (2)
- # funcool (9)
- # graalvm (29)
- # jobs-discuss (3)
- # joker (3)
- # kaocha (6)
- # malli (5)
- # music (6)
- # off-topic (21)
- # reagent (3)
- # reitit (4)
- # rewrite-clj (8)
- # shadow-cljs (49)
- # spacemacs (7)
- # sql (23)
- # tools-deps (15)
- # vim (43)
- # xtdb (19)
Is there a way to get clj
to print out the actually-resolved deps it's using? (I'm trying to override a dependency but it doesn't appear to working -- maybe :override-deps is an alias-only thing)
it is an alias-only thing
Thanks! I just realized how it’s supposed to be used: overriding a dependency in a specific context (represented by an alias) — I wanted to use it to override a transitive dependency, but I guess that’s just what top level :deps + exclusions was to begin with
don't know if it will help but within the .cpcache dir there are files with details about deps and versions iiuc
@lvh Yes, :override-deps
is an alias-only thing, and -Stree
should show you the final, selected versions of everything.
So my colleague and I head-scratched our way through fixing a situation, and we want to give back a document, or some code, or a plug-in, or what have you, and we aren't sure where'd be best.
We're delivering a skinny jar as a "free" version of a bigger SaaS product, and so as not to give away everything we AOT'd our clojure code into a jar. First we were using straight-up compile
and the jar
command, and now we're using badigeon. We hit https://clojure.atlassian.net/browse/CLJ-1544 where we have to figure out which protocols to also AOT.
(We went with skinny and a pom.xml for the rest of the deps to keep the initial download small, and to play it paranoid-safe on bundling/licensing/etc.)
anyway, it seems like quite a rough edge and we'd like other people to benefit from it, and so I'm asking advice on what'd be the best way to get this out there ... blog post? Plug-in of some sort? Contribution to the AOT guide?
if it's possible to introspect a set of namespaces to extract a set of references to protocols (so we know which ones to include in the jar) I can see us making up a library or helper too.
I suppose another idea would be to implement the proposed fix in CLJ-1544 but as a lib or contribution to badigeon.
@rgm AOT is a thorny problem and the general advice is to only AOT whole applications, as the last step before deploying them, and to avoid AOT'ing libraries at all costs. At work we don't even AOT our applications -- it's just not worth the hassle.
If your product is SaaS then maybe making a full JAR and AOT'ing all of it, so it could be run directly (but could also still be used like a library, much like REBL from Cognitect), would be a safer approach?
Writing it up on http://ClojureVerse.org might be a nice way to put the information/process out there for the community, as well as generate some follow-up discussion -- or on your own/your company's blog and then posting a link and summary to ClojureVerse?
yeah, maybe our own blog is the way to go, if only so we actually lock down our own understanding.
as far as I can tell we successfully managed a thin AOT'd jar, and it was certainly educational, but yeah, not sure I'd try that again.
The problem with AOT'd libraries is that you often end up tied into specific versions of your dependencies (including Clojure itself) so the library can be very brittle -- and may break with any change in the dependencies used by clients of your library.
I'll be interested to read your blog post @rgm and to hear any experience reports from folks who try to use your "thin AOT'd jar" but I expect pain 🙂
Then I'm confused about why you refer to it as a "thin JAR" -- applications are "uber JAR" by definition @rgm and AOT'ing those, AOT'ing all namespaces, is fine (like I say, like Cognitect's REBL).
Is that really true for REBL? I always felt half of the problems associated with using REBL are down to how it is AOT’d. (The other half are down to JavaFX/bundling and JVM versions.) The AOTing is fine when it’s used as an application; but when it’s used in a project with other dependencies on the classpath isn’t it problematic? e.g. there are a bunch of weird hacks that I’ve had to do with REBL to get it to work, like including deps it has that should be transitive… e.g. snakeyaml / data.csv / data.json… I always suspected that was because it was AOT’d and effectively being used as a library.
Oh, it’s more than possible I’m using the wrong terminology. OK, the distribution is a tgz, with our semi-uberjar and a list of deps in a pom.xml file. To run the app from our jar, you’d need to pull down the deps listed in the pom.xml, and then java -cp "our.jar:lib/*
…`
(I’m coming to appreciate how weird this is … it makes things much more like a py/ruby/js app distribution and sets us up for some of the same left-pad-style weirdness.).
though our instructions to yank the supporting jars into a local lib
dir gives us much the same benefit as pyenv / rbenv / nvm in terms of isolation.
What's the best way to convert unix epoch to date time?
It's giving me bad result
@sandae.macalalag What "date time" type are you trying to produce?
Correct value like in here - https://www.epochconverter.com/
(and I don't see what "bad result" you are getting from that screen shot -- please post actual code and results as text)
Oh, the return date is "1970"...
(require '[clj-time.core :as t])
(require '[clj-time.coerce :as c])
(c/from-long 1573788135)
#clj-time/date-time "1970-01-19T05:09:48.135Z"
If I convert it from the epochconverter it's giving me November 15, 2019
user=> (java.time.Instant/ofEpochSecond 1573804980)
#inst "2019-11-15T08:03:00Z"
user=>
Don't use clj-time
. Joda Time is deprecated. You should use Java Time instead -- as it says on the readme of clj-time
.
Also, per the docstring for clj-time.coerce/from-long
it expects milliseconds since the epoch, now seconds.
(! 836)-> clj -Sdeps '{:deps {clj-time {:mvn/version "RELEASE"}}}'
Downloading: clj-time/clj-time/maven-metadata.xml from
Clojure 1.10.1
user=> (require '[clj-time.coerce :as c])
nil
user=> (c/from-long (* 1000 1573804980))
#clj-time/date-time "2019-11-15T08:03:00.000Z"
user=>
I guess, I'm going to say it now from my current perspective. JavaScript has huge adoption because there are a lot of tools and blog posts around where you can find what you're looking for... Clojure's future seems bleak because there aren't a lot of code/tools and blogs in the wild. It's really hard for beginners to traverse. Clojure does it's job very well though. I hope more and more people will use it and showcase what they do. I will add my fair share as well. Writing posts about my journey.
Clojure isn't trying to be "popular". Just "really good". But it is definitely a language that is geared more to experienced developers who are tired of the problems that other "popular" languages bring to the table and want something better.
And, yes, it is hard for beginners to learn, especially if they're coming from a world of OOP and/or mutable state and assignments etc. The same is true of Elm and Haskell and PureScript and several other (mostly) pure functional languages. Because it requires a different way of thinking. Complete beginners -- who've never programmed before -- often pick up FP a lot more easily than junior devs who know Java/JavaScript.
And about the future looking bleak, Elisp appeared in 1985 and whilst perhaps your common dev won't know it, Emacs plugins are written in it 🙂 ... I agree that it's not one of those languages that will (probably ever) be mainstream, and that's fine.
being forced to write some java currently at work, I'd say clojure feels more beginner-friendly
(my opinions on Java are not very charitable so I will refrain from sharing them here)
I would like to hear that!
"Clojure and its communication forums are run by, and for, people who make things." -- https://www.clojure.org/community/etiquette
Oh. Thanks for the link.
fast feedback loop with REPL + autocompletion/static analysis from Cursive is better than slow feed back loop with compile/run/reach state you are interested in + autocompletion/static analysis by IDEA java plugin
Anyway, I have a problem in that clojure.core/compile
is not working for me.
What I get is
`Syntax error (IOException) compiling fn* at (compiletestfile.clj:1:1).
Datei oder Verzeichnis nicht gefunden`
how do java devs find libraries these days?
google/github/mvn search?
but seriously, those would be my guess
I’m trying to see if anyone has implemented a java.nio.file.FileSystem Provider that is backed by a local directory… this is my most promising find so far: https://github.com/peter-mount/filesystem
@rickmoynihan what's the use case?
for that class or backing by a local directory?
Main use case is abstraction… I’d like the code I write to work with a zipfile or a local directory or potentially an s3 bucket.
Update: The problem has been fixed, having `Clojure.read("'minbedrift.auth")` was wrong. The code was updated as suggested.
Hi! What are best practices for exposing Clojure functions to Java? I have created a Java class Auth
with a static method calling clojure like this (notice the require
):
public class Auth {
private static final IFn authTokenFn;
static {
Clojure.var("clojure.core/require").invoke(Clojure.read("minbedrift.auth"));
authTokenFn = Clojure.var("minbedrift.auth/auth-token");
}
public static String getAuthToken() { return (String) authTokenFn.invoke(); }
}
but clojure.lang.RT.load
fails at runtime with
> FileNotFoundException: Could not locate 'minbedrift/auth__init.class, 'minbedrift/auth.clj or 'minbedrift/auth.cljc on classpath.
But I see the file in the jar: $ unzip -l build/libs/clj-common-0.1.0-SNAPSHOT-all.jar | egrep 'minbedrift/(Auth|auth)'
1299 11-15-2019 15:26 minbedrift/Auth.class
2196 11-15-2019 15:26 minbedrift/auth.clj
How is it possible the class cannot find a file just next to it? Is require
the wrong call to make? Should I use one or the load*
functions instead? Thank you!you don't need to ' in that code
you should do the authTokenFn init in the static {} block after you require
I don't think either of those correspond to the error though
require should be fine here
I guess I would check in your Auth initializer whether you can do Clojure.class.getClassLoader().getResource("minbedrift/auth.clj")
Thanks! I have updated my post to indicate that the problem is fixed and the cause.
(do
;; random spec that exists
(s/def ::unrelated string?)
;; spec for a map that I don't care about the contents
(s/def ::map-of-who-cares (s/keys))
;; this returns false, is this expected? Seems like a bug to me
(s/valid? ::map-of-who-cares {::unrelated 1}))
Is this intended, seems like a bug to me?Thanks for the link
(s/valid? (s/keys) {::unrelated 1})
will show the same result -- this design is intentional
I mean, I would expect those to be consistent but seems there isn't a way to spec a map that only validates on the things I care about at the time.
`(is (= true
(s/valid? (s/keys :req [::the-actual-key-i-want-validated])
{::the-actual-key-i-want-validated "valid-value"
::unrelated 1})))`
Also this kind of implies that specifying :opt
is completely pointless?
spec isn't only about checking values, but also randomly generating them.
If I would users give access to a limited set of classes, say String
in an interpreter, can they, via chained method calls, do harmful things, like (System/exit 0)
?
I could build in an extra check if the method call still happens on the "allowed" classes, but I want to know if this is necessary
So, if you let them get attributes on the class, they can do something like String.class.getClassLoader().defineClass(...)
, which gives them arbitrary bytecode execution. If you're careful with whitelisting in terms of allowed classes, values, and methods, you might be good though.
What’s the easiest way to write an anaphoric macro that creates a resource binds it a symbol/name of the users choice, and releases it afterwards? e.g. from the user perspective I’d like:
(with-temp-file temp-file
(println temp-file))
It’s been a while since I’ve written one of these; but my usual tricks of resorting to ~'sym
and gensyms sym#
to avoid the ns qualified symbols of syntax-quote don’t work; because I also need to rewrite the form in & body
.
I’m pretty sure in the past I resorted to either a custom syntax quote implementation to do this sort of thing or a postwalk-replace
of form. Are there easier ways?Maybe I'm being dumb but:
(defmacro with-temp-file
[sym & body]
`(let [~sym (println "creating thing")]
(try
~@body
(finally (println "releasing-thing")))))
@rickmoynihan something like this? https://gist.github.com/lukaszkorecki/888cf7413057d5fb532262ff3f0aaac5 I might be missing some details of course
hmm that was essentially the first thing I wrote; but it was failing at the time because the symbol in the let expansion wasn’t a simple-symbol?
though now it seems to be working — must’ve missed something… 👀
ok weird, I’m not seeing the same error now
hmmm looking at this further it is weird that
(macroexpand-1 `(with-temp-file foo (println foo))) ;; =>
(clojure.core/let
[my.ns/foo (my.ns/create-tempfile)]
(clojure.core/println my.ns/foo)
(my.ns/delete my.ns/foo))
Yet taking that expanded form and evaluating it directly results in the spec error:
Syntax error macroexpanding clojure.core/let at (*cider-repl repos/muttnik:localhost:51301(clj)*:850:43).
my.ns/foo - failed: simple-symbol? at: [:bindings :form :local-symbol] spec: :clojure.core.specs.alpha/local-name
my.ns/foo - failed: vector? at: [:bindings :form :seq-destructure] spec: :clojure.core.specs.alpha/seq-binding-form
my.ns/foo - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-bindings
my.ns/foo - failed: map? at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-special-binding
doh! Thanks. It’s been a while.
(for the longest time I've just been okay using atoms so agents and refs still elude me a bit)
The documentation page on agents I link says this: "The state of an Agent should be itself immutable (preferably an instance of one of Clojure’s persistent collections), and the state of an Agent is always immediately available for reading by any thread (using the deref function or reader macro @) without any messages, i.e. observation does not require cooperation or coordination." https://clojure.org/reference/agents
I would say it's ok, but it's a bad habit. You'll miss some logs and debugging is going to be hard.
Not sure if that addresses your question about "interior mutability inside an agent".
I'm making a gen-class, and it seems to me that the reflector isn't finding the overridden method of superclass
I have :exposes-methods {"getCurrentContext" "getCurrentContextSuper"}
and I call (.getCurrentContextSuper this), and the reflector refuses to recognize getCurrentContextSuper in this.
I don't see anything in the docs that say whether all actions for the same agent can be executed in different threads, or are guaranteed to be executed in the same thread. I would assume different send action functions might be executed in different threads, so if any of the internal state of the agent is mutable, you would be reponsible for ensuring that the mutable state was properly synchronized between the threads.
and you'd be doing so explicitly against the recommendations in the documentation, so not on terribly solid ground in doing so.
I wrote atoms once inside an agent, because I used the agent as a thread and used the atom to signal the state. It was bad in every way, it didn't crash and everything worked fine. But the value of the atom was a complete black box.
But at least changes to an atom are safely published to all threads that try to deref them, so you were probably on reasonably safe ground in terms of the Java memory model, at least, avoiding data races.
You can do side effecting things in agent actions and in fact because the stm is agent-aware (agent sends in a txn are held till the transaction completes), this is a pretty useful hook
I would heed the warning to use immutable data as agent state but you could have nil as the state and (safely) update other state, either by using a state construct, locking, etc
Does the implementation guarantee safe publishing of data mutated in an execution of an action function to all other threads somehow?
Explicitly, no, I don’t think so
Practically, maybe, but just don’t do that
So swap! on an atom is safe, as are many other kinds of mutations, by their implementations, but a lot of arbitrary mutations of Java objects would be unsafe, I think.
I've started playing with macros. Oh. My. God. This changes everything :shocked_face_with_exploding_head:
I've written a single-future
macro that puts the future in an atom, in a concurrent-safe way such that only one future is ever running for that piece of code.
(defmacro single-future
[future-atom & args]
(list
'swap!
future-atom
(list 'fn '[old]
(cons 'do
(list
(list 'when (list 'future? 'old) (list 'future-cancel 'old))
(list 'future (cons 'do args)))))))
back tick would clean that up a lot
(defmacro single-future
[future-atom & body]
`(swap! ~future-atom
(fn [old#]
(do
(when (future? old#)
(future-cancel old#))
(future ~@body)))))
I don't think that needs to be a macro, could just be a function
the shape would be a bit different
Wouldn't the body
be evaluated as args unless it's a macro, and defeat the purpose? :thinking_face: Thinking out loud here...
you could put it in a fn and call future-call
or something like that
it wouldn't be as pretty, but I don't think there's any reason it couldn't be a function
Out of curiosity, is this a situation where we should say, "Well this could be a function so it should be a function"? Or is the macro justified by the value in a more ergonomic interface?
I confess I still struggle to find situations where only a macro fits, where using a function just isn't possible (do those actually exist?).
And with my basic understanding, aren't built-in macros like with-open
technically possible to write as functions?
If you want to create a construct that conditionally evaluates some of its arguments, e.g. like if
or cond
do, that is easier with a macro. I think it is only possible to implement as a function if all of the conditionally-executed things are wrapped inside of functions.
It isn't easy to see how one would create something with local bindings, like let
, without a macro.
one very pithy way to put it is that macros are for making new syntaxes (where syntaxes are ways of defining conditions for evaluation or local bindings etc.) - macros can also create namespace level bindings via def / defn which is problementic vis functions, and generally make things that delegate to other macros that treat symbols in a special way that doesn't work for function indirection
I found even when macros are great, application developer barely needs them. Most of the time, they are for library developers and only in special cases (like adding pattern matching, core.async, etc.)
right - cases where you wrap something else that is a macro, so effectively "contagion" of macros
I wonder if it can even become confusing... If a function in a library is actually a macro and it's not extremely obvious from the documentation, whoever's using that function might be blindsided by the args not evaluating at the expected time. Maybe. :thinking_face:
On the other hand, in the concrete example I created, I'm deriving a special form of a future
, and future
in itself is a macro. If I was to release my code, it could make sense to keep it as a macro for reasons of developer familiarity, even though it doesn't have to be one.
as is somewhat common, future
is a macro built over the top of a function, future-call
this is a common pattern, because it's pretty useful
it's not applicable in every case of course
here if your goal really is to provide something future-like but with differing capabilities, then I agree I would make it a macro to match the syntax expectations of future
users
Basically I started seeing a pattern in my code: I had background loops which I wanted to only ever have 1 thread going over that particular piece of code. The way I was doing this was by putting futures in atoms with the code above, but it's quite a bit of cruft to have around existing business logic. So I decided this would be my first real venture into macroland 😄
another angle here in case no-one mentioned it, future-cancel only works on a specific set of methods, and can't generally be relied on
in your case if future-cancel was called, and the active code wasn't cancellable, you would quietly create a second future, or more too the point, create a second future without the first exiting
That's a fair point. I thought about this, and I couldn't come to a conclusion on what I'd want to happen in case the future isn't cancellable/does not cancel, so I chose the default behaviour of "sticking my head in the sand" and ignoring it 😄 . But yeah, there's potential for bad behaviour.
clojure.core.reducers/fold is really the only parallel reduction in Clojure
I've got a weird issue here:
(defn normalize-classes [classes]
(let [sym->class (transient {})
class->opts (transient {})]
(reduce-kv (fn [_ sym class-opts]
(let [[class class-opts] (if (map? class-opts)
[(:class class-opts) class-opts]
[class-opts {}])]
(prn "SYM" sym (type sym) class (type class)) ;; outputs: "SYM" java.lang.ArithmeticException clojure.lang.Symbol java.lang.ArithmeticException java.lang.Class
(assoc! sym->class sym class)
;; storing the physical class as key didn't work well with
;; GraalVM
(assoc! class->opts sym class-opts)))
nil
classes)
(let [sym->class (persistent! sym->class)]
(prn (contains? sym->class 'java.lang.ArithmeticException)) ;; false, huh?
{:sym->class sym->class
:class->opts (persistent! class->opts)})))
On line 8 it prints a symbol that it's going to add using assoc!
to the transient map. On the third line from the bottom it prints false
which indicates it wasn't added...assoc! on a transient map can return a new map — it doesn’t necessarily update it in place
sometimes transients do mutate the Java object given, in place, so people easily make the wrong inference from small experiments in the REPL.
yeah they're not meant to be used like variables. they're meant to be used like normal data but with a big loud asterisk that they're not always (or even usually) persistent
this has bitten me a few times — I think transient vectors and sets don’t have this behavior
I meant the current implementation doesn’t — at least I’m pretty sure that’s the case. But yes, it’s not correct to treat it like it’s update-in-place
If we had a Rust borrow checker, it could catch this mistake, but that is asking a lot.
I'm not seriously suggesting trying to change Clojure in any way like this 🙂
actually @borkdude you've discovered a great lint check here. any time someone doesn't use the return value of conj! pop! assoc! dissoc! disj! it's probably an error
at least I am pretty sure it does, among warnings that other functions that should always have their return value used, not ignored.
It uses that big map of data you borrowed and enhanced, @borkdude, IIRC
Key :warn-if-ret-val-unused
, e.g. here: https://github.com/jonase/eastwood/blob/master/resource/var-info.edn#L200
Same warning applies if key :pure-fn
has value true, too. conj!
is not a pure function, so I created separate keys for those two properties.
There was already an issue for this (unused constant/pure values), but now it's one extra puzzle piece falling in the right place
And please take my (cough - eastwood does) comments as me attempting to be mildly funny. I am perfectly happy if clj-kondo becomes better in every way than Eastwood ever got.
This was an older issue about more or less the same: https://github.com/borkdude/clj-kondo/issues/298
Is there a good way to select keys in a map based on a namespace? E.g. you can specify #:foo{:bar 1 :x/y 2 :baz 3} and then use (select-keys my-map [:foo/bar :foo/baz]) to get those keys. Any way to use (select-keys my-map [#:foo]) to select all keys undernead the namespace :foo ?