This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-03-10
Channels
- # announcements (1)
- # babashka (44)
- # beginners (188)
- # calva (37)
- # chlorine-clover (28)
- # cider (12)
- # clj-kondo (40)
- # clojars (1)
- # clojure (323)
- # clojure-austin (7)
- # clojure-europe (20)
- # clojure-italy (4)
- # clojure-nl (16)
- # clojure-spec (7)
- # clojure-uk (37)
- # clojuredesign-podcast (1)
- # clojurescript (30)
- # cryogen (2)
- # cursive (30)
- # data-science (1)
- # datomic (26)
- # emacs (1)
- # events (1)
- # figwheel-main (13)
- # fulcro (89)
- # garden (1)
- # graalvm (20)
- # graphql (8)
- # jobs (1)
- # jobs-discuss (1)
- # joker (6)
- # kaocha (125)
- # lambdaisland (1)
- # meander (42)
- # off-topic (18)
- # pathom (3)
- # pedestal (6)
- # shadow-cljs (55)
- # spacemacs (21)
- # sql (18)
- # tools-deps (8)
- # uncomplicate (2)
- # vim (1)
- # yada (3)
Given (defn foo ^Number [^Int x ^Int y] (+ x y))
, for example, how can one access those type hints from the resulting (var foo)
? I can see the arglist info in e.g.:
{:arglists ([x y]), :line 1, :column 1, :file "NO_SOURCE_PATH", :name foo, :ns #object[clojure.lang.Namespace 0x1d4664d7 "user"]}
(Sorry, hit Enter too early; meant to change Int
to Integer
. Joker uses the former, and that’s what I’m used to lately.)
user=> (meta (first (:arglists (meta #'foo))))
{:tag java.lang.Number}
user=> (meta (first (first (:arglists (meta #'foo)))))
{:tag Integer}
user=>
(defn foo [^Integer a] a)
(meta (ffirst (:arglists (meta #'foo))))
;;=> {:tag Integer}
(defn foo ^Long [^Integer a] (long a))
(meta (ffirst (:arglists (meta #'foo))))
;;=> {:tag Integer}
(meta (first (:arglists (meta #'foo))))
;;=> {:tag java.lang.Long}
Those work in Joker, too. I’m looking into enhancing Joker documentation generation to include those tags in the docs.
Here’s the result of that work: https://burleyarch.com/joker/docs/amd64-darwin/joker.core.html#_functions This reflects a merge of a pending PR for Joker, plus pending further changes waiting for that first PR to go in, to my own fork/branch. The main change I made, in relation to the discussion on this thread, was to produce “usage” examples that look like this:
^Number (* ^Number x ^Number y)
(Sorry, hit Enter too early; meant to change Int
to Integer
. Joker uses the former, and that’s what I’m used to lately.)
Any reason why IFn can not be refactored into a Protocol in a backward compatible way for new releases of Clojure?
A protocol is an interface + more stuff, so it can only approach there performance of an interface if the jit can remove the more stuff
Which, there are written in such a way as to try and make that as likely as possible to happen
Hum... I guess I assumed that when the function class was generated and loaded by Clojure, it would just be generated where the class extends the interface of the protocol and the method is overloaded for different types
a protocol callsite has to have extra stuff, because not everything that satisfies a protocol implements the interface
Hum, ok, I think I see it:
@Override
public Object invoke(final Object gf__a__9260, final Object gf__b__9261) {
final Object cache__7950__auto__9264 = __methodImplCache;
final IFn fn;
final Object f__7951__auto__9265 = fn = ((MethodImplCache)cache__7950__auto__9264).fnFor(Util.classOf(gf__a__9260));
if (fn != null) {
if (fn != Boolean.FALSE) {
final IFn fn2 = (IFn)f__7951__auto__9265;
this = null;
return fn2.invoke(gf__a__9260, gf__b__9261);
}
}
final IFn fn3 = (IFn)((IFn)const__0.getRawRoot()).invoke(this, gf__a__9260, const__1, G__9251);
this = null;
return fn3.invoke(gf__a__9260, gf__b__9261);
}
Seems it creates a Map of type -> function, and it first checks for a function for the given type in that map, and if it doesn't find it, it calls normal invoke
A defrecord or deftype will implement the interface, so it will not find an entry in the map, and it'll then use JVM dispatch on type. But if you extend an existing type, it just adds an entry for that type in the map, and so when the function is called, it'll find the implementation for it in the map and call that.
So I think if IFn was a protocol, you'd always need to do that extra map lookup. And all calls to an IFn would actually first call the invoke that does that check, and then call the implementation found, which adds 1 extra function call I think.
(def a 1)
(def b (partial println "!" a "!"))
(with-redefs [a 2]
(b a))
! 1 ! 2
with-redefs
and biding
don’t work with partial
. Is it intented? I didn’t expect this.I will be more surprised if partial did delayed bindings. Depends on our expectations.
why? if I will change partial
to function it will work. So why no with partial
? It mean’s I can’t use with-redefs
and be confident if all places in the code use this value.
so just thinking if it is a bug or intended, but I don’t see any reason how it can be intended
With the normal function case, the lookup of value of a
happens when b
is invoked. With partial
, the lookup of a
happens when partial
is invoked. I would expect the same thing.
I’d personally always expect arguments to partial to be eagerly evaluated. The body of a partial isn’t intuitively the same as the body of a function definition, but obviously your intuition differs and it certainly could have been implemented as you expect.
For example if you pass a deref’d argument to partial, do you feel it should be dereffed immediately or on invocation?
while using with-redefs
with tests it make a concert to not use partial
at all in such case. You never know when somebody will use partial
in any place of the code with value which you want to change
just saying: I understand the simplicity for compilation behind partial
but considering the consequence for testing and with-redefs
I don’t know if I like this optimization.
so you expect to use with-redefs
to change value, but not trust this function, because in some cases it wouldn’t work
well I mean I understand technically why it is done in that way, so just because partial
is a fn
which return new fn
and doesn’t refer to value
anymore, but created new place for this value.
But still it makes me concern what to do with this. I mean it makes tests not so confident as before.
How can you be confident that others will always call your fn
with a
as parameter? What if they pass c
? Or a constant?
Especially if you’re hoping that it travels back in time to before the test is even executed.
there is a repo that collects some unintuitive repl/clojure behaviors you should probably be aware of: https://github.com/aredington/clojure-repl-sufficient-p
it uses comp
instead of partial
for behavior you @kwladyka are observing: https://github.com/aredington/clojure-repl-sufficient-p/blob/master/src/clojure_repl_sufficient_p/comp.clj
so to correct myself: I understand how this happen. The solution is not to use partial
when loading ns
, so for example in def
You could also be clever about how and when you load namespaces within your tests, perhaps.
I got a wrong impression on the beginning why this doesn’t work. So got wrong conclusions.
Hi! Is anyone have problems with clojure 1.10.2-alpha1 and io.pedestal/log 0.5.7 (latest) ? If I switch to clojure 1.10.1 everything works fine. With clojure 1.10.2-alpha1 when I call any macros info, warn etc. (example:
(log/info :msg "hello")
I have an exception Unexpected error (NoClassDefFoundError) macroexpanding log/info.@mike1452 could it be that you have some AOT-ed classes around? did you try lein clean, rm -rf target, etc?
@borkdude I will try...
@borkdude Thanks!! It works now. clean target folder is a solution.
I keep running into issues where in the component reset stops working if i am running a figwheel for the client
Hello everyone! Have you already tried to create a "listener" to some function? For example: I would like to recur a function that always check if there is a new message on activemq. I'm looking into documentation to find something suitable to my needs, but no insights for now
That sounds like you want your listener on the queue itself. You can add watches to ref types: https://clojuredocs.org/clojure.core/add-watch. That help?
I think so. Let me read this. Thanks 🙂
Is this lib abandoned https://github.com/ptaoussanis/timbre ? do you know what happened to the author?
looks like nothing happening...
PRs and issues to be worked on. People does contribute but nothing is merged
It's a year old now, but this was from the author after a similar question: https://github.com/ptaoussanis/timbre/issues/279#issuecomment-467422874
So at a guess, it's not considered abandoned by the author but hasn't had the time to dedicate to it for a while.
But there doesn't seem to be a huge amount of activity. Having said that, reading through some of the issues leads me to think that the core functionality itself is actually pretty solid. That's certainly my experience of using timbre over the last few months having switched from a slf4j+logback+tools.logging setup.
One thing that’s kind of cool about “the clojureverse” is that there are a number of libraries that reach a certain point of stability and then just stay there. It’s not so much that they’re abandoned, but rather there’s little or no significant development left to do. It does what it set out to do, and it’s ready for prime time.
Clojure is pretty extensible in user space. So most libraries don't need to accept new things.
is it known and intended that transit cannot encode keys with metadata? See https://nextjournal.com/mk/transit-metadata-keys for a minimum repro.
You will probably get a better answer and leave something easily searchable if you open an issue in the transit-clj repo. I couldn't find anything that mentions metadata on keys.
clojure.set
is a great example. Functions from there will work on a great variety of collection types combinations. But they're intended to be used only with sets.
BTW it seems that it's not just a metadata issue. It's an issue of using a symbol with metadata. Try using a map with metadata as a key, for example. Judging by the code, it should work just fine.
Ah, my bad - I was writing to the same buffer multiple times. It is read back, with the metadata.
Transit isn't meant to encode Clojure data though. It is meant to encode EDN. And EDN doesn't know metadata.
Transit doesn't have to do anything with EDN. Those are completely different formats.
But to be fair, it also doesn't have to do anything with Clojure - there are many implementations for different languages.
transit-clj added support for metadata in https://github.com/cognitect/transit-clj/commit/a823f3b418e535232fa48f0b73ac8e32c4818863 and I believe people are using it to encode Clojure data. I don’t get your point.
It was added for some ad-hoc case. There's no metadata in the spec, there's no metadata support in transit-python
, for example.
yes, it was added for Clojure metadata in transit-clj and it’s using a tagged array as a base type.
I am astonished about this article. They seem to advise that we leave the sinking ship: https://www.beseen.com/blog/talent/top-tech-skills-2020/?sid=us_eml_JS_ACQ&kw=pockethits_TXT_skills-tool&utm_source=eml&utm_campaign=NP-Seen-us-JS-pockethits-newsletter
*sinking
Agree. I can't move it though.
And it seems that there's no way to get my hands on the original data. Which already makes me want to throw that "study" into a garbage bin.
it requires higher CPU utilization (which drives up hiring and operating costs) - what does this even mean?
Operating cost - well, you now have to pay more for electricity. Hiring cost - uhm... well, maybe your hiring software is written in Clojure, and then it still makes you pay more for electricity. :)
Well, I'd argue that Clojure should indeed have higher CPU utilization than Java simply because it's mostly an abstraction on top of it. And regarding how affects - the electricity thing is true. Anything else - assuming proper maintenance schedule, no idea. Nothing else should really change.
Hiring costs refers to steep learning curve.
Y-axis seems to be number of job ads.
> Hiring costs refers to steep learning curve Ah, right. The phrasing is a bit off. And I'd argue that the learning curve is much less steep than with Java or even Python. > Y-axis seems to be number of job ads. It displays values between 0 and 1. And the current job number is in the hundreds. So it should be something else.
These might be mere perceptions or tentative, and wrong, explanations. Y-axis could be normalised.
The very first plot has "percentage of all job posts" as its Y axis. So that's probably that. And if that's indeed the case, then they just measured relative popularity on a single platform. Good job 👏 throws the study into the garbage bin
The chart also exaggerates the decline by not using an absolute scale (0 to 100). Some tech is just not mainstream and it's likely never going to be. Doesn't mean you can't or shouldn't do it. The ship is certainly not "sinking" where I work and I think it is a mischaracterization to say that it is in general.
I can very much assure you that every metric I am aware of for Clojure continues to go up - downloads, unique IPs downloading, web site usage, tool installs and usage, etc etc
Are there any published metrics illustrating this? It'd be good to share them with the engineering teams where I work. It sounds like they're likely internal to Cognitect though, right?
nothing I can easily share
50% increase
It's going up where I work!
so, apply grains of salt appropriately
Isn't that a classic example of "how's paying you to say this?" - they are some kind of hiring proxy/agency/whatever - it would be obvious to push their audience into mainstream skills as that increases the chance of landing a job through them (or whatever they do).
Is there a way to find out what has changed between clojure-tools 1.10.1.507 and clojure-tools 1.10.1.536?
Got it, sort of. Are you looking for changes in clojure between those jars or the diff in tools.deps.alpha?
No easy answer in either case, but that'll help me help you get your answer.
Sure, I appreciate it. What I'm trying to do is to understand what has changed between 507 and the latest 536 - only to understand whether it's worthwhile to open a "outdated" ticket on Arch to request an update.
Exactly!
In cognitect-labs repos that's based on something documented in the repo, but I don't see anything the clojure repo.
Still looking
Thanks @U064X3EF3
also often relevant: https://github.com/clojure/tools.deps.alpha/blob/master/CHANGELOG.md
@U11EL3P9U that give you the detail you need?
if there's someplace you looked that that link could have existed, let me know so I can update
@U064X3EF3 part of the challenge is tying the clojure version to the tools.deps.alpha version
or vice versa, rather
Heh, that's interesting how the Linux script has versioning that depends on the Brew script but at the same time it itself doesn't depends on Brew at all.
well, it's just all released together, windows too
but that ended up at tools.deps.alpha, but with the changelog at a different version number
gotcha, I'll see if I can add some links in a few more places
the brew-install repo is poorly named but I've been too lazy to change it and fix all the build automation
thx, that helps
The second face of "naming is hard" - you also have to change all the references if you change the name.
but now I know where it links the homebrew install script (called clojure-tools!) (references (indirectly) to clojure.tools
I can raise an outdated on arch now, since 0.8.677 (which is in 1.10.1.536) contains some fixes
Do people have preferred naming conventions for returning a value vs returning a map with the value added? For example, what would you name these two functions?
(defn person-name-1 [m]
(str (:first-name m) " " (:last-name m)))
(defn person-name-2 [m]
(assoc m :name (str (:first-name m) " " (:last-name m))))
I would probably not write the second one and instead just assoc using the first one
Do I remember correctly that there was a function for turning sequence [:a :b :c]
into map {0 :a 1 :b 2 :c}
?
(into {} (map-indexed vector [:a :b :c]))
=> {0 :a, 1 :b, 2 :c}
FWIW I meant to use a transform above like this:
(into {} (map-indexed vector) [:a :b :c])
Just didn't want to leave a bad example above.I guess more specifically my case involves threading a map through a few functions where prior functions add things that later functions use.
Hello. Perhaps this is a weird question, but I couldn’t find any documentation in the internet. What is the way to extend generic Java interface in clojure? For example if I want to create a stack type that should extend Iterable<Item>?
So I can just extend Iterable, right?
Great! Thank you very much
Also is there some documentation about core clojure interfaces. If I want to implement some new datatype, how can I make for macros (or other collection operations) work with it?
There are example implementations of new data types that do this I could point you at the source code of, but there is nothing I am aware of that says: "to implement a new kind of map, here are the 11 interfaces you should implement"
@U0CMVHBL2 something is better than nothing. Could you please point out to example
As a couple of example, there are these libraries: https://github.com/clojure/data.priority-map implements a new kind of map, and https://github.com/clojure/core.rrb-vector implements a new kind of vector. The code for the first is significantly shorter.
for a macro to use your datatype, you'd need a datareader (and the macro would use the data that the reader emits) - otherwise you'd just use a macro to emit the code which uses your data structure
Note: Depending on what you want to do, implementing a new data type may be more work than you need to go to.
Not many Clojure developers do that.
there is of course defrecord
which just does the right thing for the use case of "a hash map with behaviors"
This just a learning thing. I want to work out Sedgewick’s “Algorithms” book, but with Clojure
Here is a toy bag/multiset implementation I did while trying to learn the exact same thing as you are. https://gist.github.com/Hindol/c92a768f7f784379dc8bcd546a60fa42
Great! Thanks @UJRDALZA5
I've got an example project that makes Java from Clojure. There's a relevant file here https://github.com/markbastian/clojure-makes-java/blob/master/src/interop/core.clj. Basically, reify makes it pretty trivial to implement interfaces on the fly.
Iterable isn't something you generally need to extend, Clojure vectors are already Iterable.
you can pass [item1 item2 item3]
to a function taking Iterable, and Java happily accepts it
I want to implement stack, queue, binary tree, 2-3 tree, red-black tree and a lot of other crazy things. And this is just a learning exercise, so I want to do it low level and all by myself (no libraries)
https://github.com/clojure/data.avl/blob/master/src/main/clojure/clojure/data/avl.clj#L175
working from a traditional algorithms book where mutability is assumed this is going to be fun in clj 🙂
Yes, a lot of fun 😂
Thanks a lot @U050ECB92
This file contains a mutable implementation of Tarjan's algorithm for finding strongly connected components in a graph, using mutability for trying to maximize efficiency. At least the primary result of the components is returned as an immutable Clojure vector, but it also returns some intermediate results as mutable Java arrays. Even the original pseudocode on this one wasn't easy to follow, since it creates explicit stacks to avoid recursion in the function calls: https://github.com/jafingerhut/cljol/blob/master/src/clj/cljol/ubergraph_extras.clj#L460-L674
(mapv #(Item. %) collection)
<- can pass that as Iterable<Item>
(I don't know what Item is, assuming it's just for the example)
I'm not sure if this doesn't exist, or I'm just failing to Google it: can I get my REPL to show all the protocols some object supports?
There is no way to answer that
Some third party could add an extension at any time and that is stored in the protocol, not in the object
ah, I understand.
And if Fn is overloaded to multiple arities all returning the same type, can the var hint be used for all of them?
on the var "happens to work" iirc. documentation says the vector https://clojure.org/reference/java_interop#typehints
Right, but someone else pointed me to https://clojure.org/reference/special_forms#def
Which says: > :tag - a symbol naming a class or a Class object that indicates the Java type of the object in the var, or its return value if the object is a fn.
user=> (defn ^Object foo (^String [] "foo") (^String [x] (str "foo" x)))
#'user/foo
user=> (.length (foo))
3
user=> (.length (foo "a"))
4
user=> (defn ^String foo (^Object [] "foo") (^String [x] (str "foo" x)))
#'user/foo
user=> (.length (foo))
Reflection warning, NO_SOURCE_PATH:1:1 - reference to field length on java.lang.Object can't be resolved.
3
user=> (.length (foo "a"))
4
as to second question, yes:
user=> (defn ^String foo ([] "foo") ([x] (str "foo" x)))
#'user/foo
user=> (.length (foo))
3
user=> (.length (foo "a"))
4
yes, arglist is the preferred place
Good to know! For pure curiosity, any known history as to why it evolved to have both?
separate design decisions at different times, I don't know the details
they are not identical though - the var type hints are evaluated (so things like ^long will evaluate to the long
function object, which is not good). the hints on the arg list are not evaluated.
Check out a new early access release of Reveal (Read Eval Visualize Loop): https://twitter.com/v1aaad/status/1237507561126940679 I use it at work instead of a usual repl output panel and it's so much better than regular repls!
Is there a way to remove a ns of a keyword. #:festival{:festival-id "1", :name "My cool festival", :type "Cool"}
so the result would be {:festival-id "1", :name "My cool festival", :type "Cool"}
user=> (set/rename-keys m (zipmap (keys m) (map #(keyword (name %)) (keys m))))
{:a "a"}
traversing it twice to build the map to pass rename-keys, only to have rename keys traverse it again
@U0NCTKEV8 what would traversing once look like? So I can compare with what @bfabry shared.
in the world I work in that will never ever matter, but yes it is slower than
(into {} (map (fn [[k v]] [(keyword (name k)) v]) m))
I’m working with a couple million maps. So the faster one makes sense :P
or for example, if you are serving data as json do the namespace removal as part of the json encoding since that has to traverse the map anyway
I’m considering that doing vals
could be sufficient if I approach this problem the right way. The output is CSV. But I’m looking for a first pass atm and my CSV function works with maps for now.
if you are already aware of the bounds of your problem ignore me, but fwiw CSV is actually a way more complicated spec than you'd think and if you have to interface with something I'd use data.csv
For context. I’ve taken messy data. Performed transforms on them with Meander applying namespaced keywords. Transact them to datahike because it’s too much data for memory. Then use datahike for joining data together. Now I just want to do final cleanups before export.
you take the first map, write the columns out from there, and then rest just write the values in the same order without having to do anything to the keys
Hmm I guess I’m doing double steps here.
(defn generate-csv
[data]
(let [columns (-> data first keys)
headers (map name columns)
rows (map vals data)
writer (.StringWriter.)]
(csv/write-csv writer (cons headers rows))
(.toString writer)))
But I’m okay with this for now. My concern was that maps are not ordered? Or is this just sets?
So I can’t guarantee that if I do a vals on it. The result with line up with a pre-defined “header list” I create.
but it is very likely (not guaranteed) that maps that contain the same keys will be iterated in the same order
I mean my generate CSV function kind of depends on that behaviour of it being consistent.
store the first set of keys, do (map (fn [row] (map #(% row) header-keys)) data)
instead
@bfabry that looks more like going from CSV to maps?
nope. given a list of keys called header-keys and a list of maps called data that returns a list of lists with the values of data in the order of header-keys
Hmm must be time for bed for me then haha.
I’m pretty tired. I will have to throw some sample data at it in the repl.
So simple and I don’t get it. Definitely tired haha.
I’m not convinced that code actually works lol
Oh I see. It’s a seq of maps being passed in.
So you destructure a seq of maps using a vector of header keys which ensures they are placed in the right order.