This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-06
Channels
- # announcements (5)
- # aws (28)
- # babashka (4)
- # beginners (163)
- # bristol-clojurians (2)
- # calva (4)
- # cider (18)
- # clj-kondo (30)
- # cljs-dev (28)
- # cljsrn (50)
- # clojure (96)
- # clojure-europe (25)
- # clojure-italy (6)
- # clojure-losangeles (1)
- # clojure-nl (4)
- # clojure-sweden (7)
- # clojure-uk (32)
- # clojurescript (39)
- # conjure (74)
- # cursive (12)
- # events (1)
- # fulcro (32)
- # helix (71)
- # jackdaw (2)
- # leiningen (10)
- # off-topic (14)
- # pathom (59)
- # rdf (7)
- # re-frame (6)
- # reitit (28)
- # ring (7)
- # shadow-cljs (207)
- # slack-help (2)
- # spacemacs (3)
- # specter (7)
- # sql (12)
- # tools-deps (14)
- # xtdb (32)
hi. I need a tiny tip. I can’t find any example (or if it’s even possible) to update ~/.clojure/deps.edn
dep versions like we do with lein ancient to update .lein/profiles.clj
deps-ancient
is based on lein ancient
but I don't think it updates the deps.edn
file? Depot
has both a report mode and an update mode so that may be what you're looking for.
find-deps
will also update your deps.edn
file when you ask it for a new dependency which may also be useful.
(personally, I don't want any tools to touch my deps.edn
file -- I prefer report-only tools, and then I can change the file myself if I want to)
@seancorfield always throwing great links 🙂 thank you very much. I was searching https://github.com/clojure/tools.cli but without success. Anyways, yes your tip is important regarding the report Vs actually update deps. In fact I am just keeping some deps under ~/.clojure/deps.edn
because.. n00b and I want to keep an eye on couple projects. (side note: I am sliding a little bit to the yak shaving side 😕 )
@include tools.cli
is a library for processing command-line arguments (that I happen to maintain). tools.deps.alpha
is the core library behind the Clojure CLI and deps.edn
. brew-install
is the Clojure repo that contains the actual scripts (`clojure` and clj
) that wrap the t.d.a. libary. And for anyone on Windows wanting to try the Clojure CLI and deps.edn
on PowerShell, see https://github.com/clojure/tools.deps.alpha/wiki/clj-on-Windows and in particular the last section that talks about Scoop (which I've found to be the easiest way to install/upgrade various Clojure-related tools).
@seancorfield tools.cli
will be very useful in my case. I am migrating some bash scripts to clojure (babashka) and I need to read args from cmdline; but I am still fighting with the basics. (and btw thanks for maintaining it 🙂) . Yes, I am using brew-install which is very useful and clean too).
“murphy” (@UBL24PLE6 just posted another deps management tool in #announcements
🙂 going to check it too.
repl question: if I’m in ns A, and ns A requires ns B, and ns B requires ns C. if I change something in ns C, what do I need to do to get ns B to reflect these changes correctly?
I know I can (require :reload …)
but if I (require :reload B)
will this pick up the new changes? do i need to reload C? do I need to reload C then B?
currently I just reload like a madman and restart my repl if I can’t seem to get it to stick correctly (which is becoming less frequent, but still happens more than it should)
One option is only re-evaluating the def
and/or defn
forms that changed in ns C, assuming you know which ones those are. If those changes were made by you via a text editor on a file containing ns C, then presumably you know which ones you changed as you were changing them.
@ryan072 I'll echo @andy.fingerhut here: if you always eval each top-level def
/`defn` form as you change it, your REPL state will stay as fresh as possible and these questions will rarely come up. That said, if a def
is used in another namespace, you will have to re-eval the forms in that namespace that refer to it (or reload the whole ns). The same goes for functions that are used as plain symbols in the non-function position in other code (if you use the Var form -- #'
-- then changes will automatically be picked up, because of the extra indirection that the Var introduces). Another option is to do (require 'ns-A :reload-all)
which will force a recompile of ns A and everything it requires transitively.
> if a `def` is used in another namespace, you will have to re-eval the forms in that namespace that refer to it this is only true for forms that use the val of the def rather than the var, if you use the def inside a defn, it is re-resolved on each call and no extra reload is required
def using another def directly would have this issue, yes
Yeah, it got a bit complex and nit-picky to be truly precise about that so I (over-)generalized a bit. The truth is: if you're careful and know what you're doing, you can get away with minimal reloading when a definition changes; but it's also true that if you aren't/don't, you can get into trouble 🙂
that's fair - related, I saw a bug where not defining an env var during AOT led to not seeing the config during prod runs, thanks to the env being looked up in def, I thought clojure prevented this sort of error - do you know anything about this behavir?
something like
(def some-config (System/getenv "SOME_VAR"))
That would be executed at compile (load) time -- which is why anything side-effect-y (including reading from external systems) is dangerous in def
forms. I'd use a delay
here or make it a function (and "cache" the result somewhere else -- probably via Component).yeah, I thought I had heard that the compiled init for the ns would capture the body of the def, but I guess I was wrong
agreed that delay or something like component / integrant is the right fix here
I try very hard to ensure my def
's only ever have either completely static content or else are bound to a delay
or atom
. And I try to keep them private, to avoid other namespaces from depending on them directly. Like all "rules" tho', I break these patterns "when appropriate" 🙂
sounds like lisp :D
Thanks to you both @U051SS2EU and @seancorfield.
And @seancorfield I know realize you are right — if I just eval defn
’s as needed I should be good to go!
I work in a REPL for days (sometimes weeks) and I eval every top-level form as I change it (without even saving) and mostly just invoke load file on my test ns after that and then run those tests (all hot keys in my editor).
Question about reflection: In my repl I do this:
dev=> (defrecord Foo [])
dev.Foo
dev=> (def bar ->Foo)
#'dev/bar
How do I get the namespace and the protocol name of bar?
eg, two strings "dev" and "Foo"
thanks @seancorfield and @andy.fingerhut.
@seancorfield for your comment about Var forms, are you saying that if I do something like
(defn a [] ...)
(defn b [] (b ...))
and if I re-eval a
, then b
doesn’t change (still uses the original a
) correct?
But if instead I do
(defn a [] ...)
(defn b [] (#'b ...))
Now b
does a “live” lookup on a
so if I re-eval a
, b
will see those changes automatically?
And if I have this correct, is your suggestion to largely use the var form (for this benefit)? or when do you use it?That's b
in the function position (where the indirection happens anyway, I believe).
I was talking about non-function positions, where you pass a function as an argument. And also where you refer to a def
directly.
I would like to contact someone named joinr. Does anyone know how to contact him? He is the author of https://github.com/joinr/clclojure
he's on reddit a lot, same user name (I believe this is correct pronoun, but apologies if not)
Sorry, I'm not a reddit user. If I create an account, will I be able to send a message to a particular user? Or will I have to request a contact with him, and thereafter be able to send him a message?
you can send a message I'm pretty sure
ah, but I have to have an account on reddit for that button to appear. I just figured it out. now I have 10,001 accounts on various social networks.
Maybe he no longer wishes to be contacted?
Reddit search is not for finding users, it's for finding content. This is his profile: https://www.reddit.com/user/joinr
what does {= ...}
mean?
clojure-rte.core> (methods ty/typep)
{= #function[clojure-rte.type/eval337/fn--339],
:default #function[clojure-rte.type/eval263/fn--264],
:sigma #function[clojure-rte.type/eval247/fn--248],
rte #function[clojure-rte.core/eval907/fn--909],
satisfies #function[clojure-rte.type/eval319/fn--321],
:empty-set #function[clojure-rte.type/eval255/fn--256],
or #function[clojure-rte.type/eval305/fn--307],
not #function[clojure-rte.type/eval275/fn--277],
member #function[clojure-rte.type/eval353/fn--355],
and #function[clojure-rte.type/eval291/fn--293]}
clojure-rte.core>
clojure doesn't have syntaxes introduced by a symbol inside a bracket
In a call to defmulti
I can provide a dispatch function. That function normally returns some dispatch value which has been given on some defmethod. Is there a way for the dispatch function to return a value which tells the system, don't call any method?
Of course I can have some method with dispatch value :do-nothing, and just have the dispatch function return :do-nothing. Is that the correct way?
that's the only option- there's also :default
, which is calledfor every dispatch with no defined method, if it exists
Is there any way for the dispatch function to pass a computed value to the :default
method?
the default method would see the original args, and you could recursively call the method from within another method
no, I mean is there a way for the dispatch function to compute a value and pass to the method?
Because the ability to do that would mean it is possible to implement CL-like method combinations in clojure
no, you can achieve that result by calling the same method again so the other dispatch is invoked
but a dispatch only provides a value to match
ahhh, so I can just have the dispatch function always return :default, and have the default method itself call methods
and dispatch to one or several of them?
that's perverse 🙂
I was thinking more that an individual method would "cook" the args, then call the same method so it hits the other dispatch, for chaining
classic "superclass delegation" but it's not restricted to going up the chain
does the language enforce any sort of argument-list consistency at the declarations of the defmethods ?
they all need to accept the args that your dispatch function accepts, or you'll hit runtime errors
superclass delegation is a good example. The :default method could presort the methods and call the first method with a call-next-method
which passes (rest sorted-methods)
to the next method in the chain.
in my case I want all the methods called lazily and a value returned which is a function of the return values of all the methods.
i.e., a method combination
multimethods aren't magic, you might have better luck with your own dispatch and registration functions
if you do it right, they could build on top of defmulti for convenience
more importantly than not being magic, they are just vars and hash table lookups, you can achieve the same thing in "user space"
yes that was my first implementation in fact. But someone suggested that I use (defmethod ...)
which already knows how to associate functions with keys. and methods
which returns that hash map
you could do what defmethod does, and combine hierarchy definition and lookup via isa? with registration in a map
I think making defmulti act with the semantics you expect is more complex than doing that yourself
That was the intent of this message https://clojurians.slack.com/archives/C053AK3F9/p1588783391100600
that link is broken
Can someone help me convert a set of functions/atoms into a more concise macro definition? I'm writing the same function several times.
(def disjoint-hooks (atom {}))
(defn new-disjoint-hook
"Establish (or override) a named hook for use in disjoint?
This is necessary because clojure multmethods do not support
call-next-method. We need several _methods_ to be called until
one fails to return :dont-know.
(new-disjoint-hook ...) establishes a new hook, which must designate
a binary function which returns true, false, or :dont-know."
[key hook-fn]
(swap! disjoint-hooks (fn [_]
(assoc @disjoint-hooks key hook-fn)))
(keys @disjoint-hooks))
(def subtype-hooks (atom {}))
(defn new-subtype-hook
"Establish (or override) a named hook for use in subtype?
This is necessary because clojure multmethods do not support
call-next-method. We need several _methods_ to be called until
one fails to return :dont-know.
(new-disjoint-hook ...) establishes a new hook, which must designate
a binary function which returns true, false, or :dont-know."
[key hook-fn]
(swap! subtype-hooks (fn [_]
(assoc @subtype-hooks key hook-fn)))
(keys @subtype-hooks))
(def inhabited-hooks (atom {}))
(defn new-inhabited-hook
"docstring"
[key hook-fn]
(swap! inhabited-hooks (fn [_]
(assoc @inhabited-hooks key hook-fn)))
(keys @inhabited-hooks))
fixed.
How can I use gensym
for a def? As in, I'd like to do (def (gensym 'myprefix) "hello")
- and I'd like symbol from gensym to be used for the def - which would result in a var defined as something like user/myprefix1234
This is for tooling where I'd like to store some information in the user's repl for later retrieval with low/no risk of var capturing. I'd return the symbol generated for reference later
intern
can do it, or you could write a macro
(ins)scratch=> (intern *ns* (symbol (gensym "auto-")) 42)
#'scratch/auto-25871
(ins)scratch=> auto-25871
42
mind you, wanting to do this is often the sign you are trying to use an ns as a data structure, which is counterproductive
> wanting to do this is often the sign you are trying to use an ns as a data structure, which is counterproductive I'm using a clojure library from typescript via nrepl to get some information about the repl environment that needs to be stored/retrieved later. Since I need to interact with that data as a whole via clojure again later, but only need a subset of it in JS, it seems to make more sense to store it in the repl rather than storing as a string or parsing it in JS, and passing back to the repl later.
if your goal is to store data, use a hash-map, you can put it in an atom in a namespace
hash-maps are good reusable convenient data structures, code written to use them is relaible
code that tries to use namespaces this way gets fragile / weird
(as a very broad generalization)
Yeah, the data is a hash-map, I just needed a place to store it that won't conflict with anything the user has defined in the repl thus far, or may define later
So I can't just def an atom with some name, because theoretically there could be a conflict then or later, no matter how unique I try to make the name, unless I use gensym (which maybe still there is a chance of conflict, but it's a standard way to get a unique name)
what I mean is a hash map with a predictable name, containing each thing you want to store this way (including hash-maps if you like)
if you need to auto-generate keys in that hash-map, you can easily use gensym etc., but the result is something less fragile than alterations to a namespace
Yeah, I get what you're saying. The discussion of my use case is maybe beyond the scope of this channel, but basically, I don't own the repl, it will have been started by some user. This is for adding variable inspection during debugging to Calva, the VS Code Clojure extension.
It's mostly written in typescript and it interops with clojure via nrepl. I get the locals from cider-nrepl during a debug session and then the user can "drill down" into each local variable that's structured (map/collection).
The orchard.inspect inspector
maps are what I need to store for each variable - so it's a choice of storing those in JS and passing it back to clojure or just storing them the user's repl and keeping a reference to them in JS. I'm still figuring out what's best there.
But that is pretty much irrelevant to the original question 😄 . Just felt like explaining
I think it's still less intrusive to introduce one variable for storage, that's first class, but of course it's your app and your design
if I see a def in an ns, I want to find the source for it
existing user tooling deletes all variables in an ns and recreates it
with a map in a var, you can make it less likely that that breaks everything
> I think it's still less intrusive to introduce one variable for storage Yeah this is what I was getting at - a single var/map for storing all the inspectors. > if I see a def in an ns, I want to find the source for it What's your implication here? I thought at first read you meant it would be wise not to store the data in the repl, as well as with your statement below that
there might be a miscommunication here - what I mean is that if you have an ns where there's something like (def contexts (atom {}))
- that's something I can look at and understand - I can look for the code that alters it, etc.
if you have something that interns new vars for contextual data, I'm more likely to be confused when debugging
Since there is no code file in this context, nowhere for the user to see where it was defined
I find "store in the repl" an odd statement - in clojure the repl is a tool that gives access to the vm, so I guess this means "store the data in the vm"
after googl’ing quite a bit I see the question I have has come up in the past, hoping maybe there’s a solution now…
after lein
has finished uberjar
(with :aot :all
) I need it to run javafx.application.Platform/exit
javafx might have a headless flag or something
like for awt, you can use -Djava.awt.headless=true to fix a lot of stuff like this
unfortunately some javafx classes require javafx to be running due to how a static initializer is setup
ok, I don't know anything about javafx :)
but surely if you google headless javafx you may find some ideas
in general, is injecting code to run in a post-compile step not something easy to do? (maybe a dumb question)
why do you need AOT? often the easiest solution is just not doing it
there's usually the option of using clojure.main
as your entrypoint, and providing "-m"
and the string of your ns name as the first args
all args after that will be sent to your main
I’m going to be running it through native-image
and from my experiments so far I need to use an AOT uberjar
oh, yeah, graalvm
If that's so, I would be interested to know about your experience, I tried compiling cljfx/hn example app with native-image, but failed so far
ugly thing about my “hello world” is that I manually built this file: https://github.com/michaelsbradleyjr/hello-cljfx-native/blob/master/resources/META-INF/native-image/reflect-config.json
https://github.com/cljfx/hn/blob/native-image/build/native_image.clj there is my work in progress for graal, I might resume it now with your findings!
also, I had to put these requires: https://github.com/michaelsbradleyjr/hello-cljfx-native/blob/master/src/hello_cljfx_native/core.clj#L3-L6
So you have to compile all of those too. I wrote a simple workaround to do it here: https://github.com/cljfx/hn/blob/native-image/build/compile.clj
but upx has downside of initial memory usage being higher because the whole unpacked executable goes into memory and that mem can’t be reclaimed
iirc, I think at startup mem usage is 130 MB, but that can probably be tuned via settings that can be passed to graal
also, I found the binary isn’t portable across different versions of macOS, but there’s probably a compiler flag for setting the min OS version to support, just didn’t find it yet
it’s been some years since I was using clojure and I don’t know the newer tools, but I remembered lein
can hook to maven
Heh, I think Clojure in the beginning/middle of transition period towards simpler tooling, first with clj/tools-deps, and now they are building tools-build... I tried with substrate the library, not substrate the maven plugin
makes sense, I just went with what allowed me to get started, since I knew lein from 2013/2014
the folks in #cljfx might know a fix for your issue
they sort of do, but it’s not a complete fix: https://github.com/cljfx/cljfx#aot-compilation-is-complicated
i’m in the situation where I really really need to run Platform/exit after compilation
ah, got it
the compile plugin isn’t that complicated, https://github.com/technomancy/leiningen/blob/master/src/leiningen/compile.clj. unfortunately, the best option might be to write your own lein plugin that works as needed