Fork me on GitHub
#clojure
<
2020-03-10
>
jcburley01:03:15

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"]}

jcburley01:03:14

(Sorry, hit Enter too early; meant to change Int to Integer. Joker uses the former, and that’s what I’m used to lately.)

jsn01:03:29

user=> (meta (first (:arglists (meta #'foo)))) {:tag java.lang.Number} user=> (meta (first (first (:arglists (meta #'foo))))) {:tag Integer} user=>

👍 4
jsn01:03:56

(if I understand the question correctly)

didibus01:03:47

(defn foo [^Integer a] a)
(meta (ffirst (:arglists (meta #'foo))))
;;=> {:tag Integer}

👍 4
didibus01:03:47

And:

(meta (first (:arglists (meta #'foo))))

👍 4
didibus01:03:01

For the return if you care

jsn01:03:10

@U0K064KQV not reading previous replies, huh? 🙂

4
didibus01:03:10

(defn foo ^Long [^Integer a] (long a))
(meta (ffirst (:arglists (meta #'foo))))
;;=> {:tag Integer}
(meta (first (:arglists (meta #'foo))))
;;=> {:tag java.lang.Long}

👍 4
didibus01:03:05

nope hadn't

jcburley01:03:44

Great, thanks!!

jcburley01:03:38

Those work in Joker, too. I’m looking into enhancing Joker documentation generation to include those tags in the docs.

jcburley23:03:27

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)

jcburley23:03:36

Thanks again for the help!

didibus05:03:22

Any reason why IFn can not be refactored into a Protocol in a backward compatible way for new releases of Clojure?

didibus05:03:33

Would it affect performance? Would something break?

hiredman05:03:04

It would effect performance

didibus06:03:10

Hum... so are protocols generally slower?

didibus06:03:59

Compared to interfaces?

didibus06:03:07

Or is it something specific to IFn ?

hiredman06:03:37

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

hiredman06:03:34

Which, there are written in such a way as to try and make that as likely as possible to happen

didibus06:03:03

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

didibus06:03:36

I probably don't understand the protocol machinery

hiredman06:03:07

a protocol callsite has to have extra stuff, because not everything that satisfies a protocol implements the interface

didibus06:03:21

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);                                                                                     
    }

didibus06:03:42

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

didibus06:03:06

That extra lookup in the map is the overhead

didibus06:03:21

Plus probably the extra function indirections involved as well

didibus06:03:49

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.

didibus06:03:15

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.

kwladyka09:03:14

(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.

hindol09:03:56

I will be more surprised if partial did delayed bindings. Depends on our expectations.

kwladyka09:03:22

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.

kwladyka09:03:58

It was so useful for tests, but now it doesn’t seem to be good idea anymore

hindol09:03:11

Can you share the example of changing partial to a function?

kwladyka09:03:09

(defn b [& args]
(apply println "!" a "!" args)
it shoud do the same what above

kwladyka09:03:21

and this time it works with with-redefs

kwladyka09:03:27

so the issue is only about partial

vlaaad09:03:05

@kwladyka does (def b #(apply println "!" a "!" %&)) work?

kwladyka09:03:27

so just thinking if it is a bug or intended, but I don’t see any reason how it can be intended

hindol09:03:36

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.

thom09:03:37

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.

vlaaad09:03:54

it is an intended edge case I think

vlaaad09:03:43

or like, normal clojure semantics which are unintuitive here.

thom09:03:15

For example if you pass a deref’d argument to partial, do you feel it should be dereffed immediately or on invocation?

kwladyka09:03:51

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

thom09:03:28

In that case you just need to swizzle the value earlier, if possible.

kwladyka09:03:48

not possible

kwladyka09:03:00

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.

thom09:03:22

I don’t think it’s an optimisation I think it’s just the least surprising behaviour.

hindol09:03:42

It is not an optimisation. Many people will expect it to behave this way.

kwladyka09:03:52

hmm interesting

kwladyka09:03:34

so you expect to use with-redefs to change value, but not trust this function, because in some cases it wouldn’t work

thom09:03:36

I’d find it vastly more confusing if (partial foo @bar) delayed the deref for example.

thom09:03:06

But I don’t find it confusing that #(foo @bar) does

thom09:03:29

partial isn’t defining a function body, it’s just saving some parameters for later.

thom09:03:04

It’s the values you care about not the references.

vlaaad09:03:42

It’s one of the reasons I usually don’t use partial

kwladyka09:03:55

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.

kwladyka09:03:22

But still it makes me concern what to do with this. I mean it makes tests not so confident as before.

kwladyka09:03:14

and yes the solution sounds to not use partial

hindol09:03:32

How can you be confident that others will always call your fn with a as parameter? What if they pass c? Or a constant?

kwladyka09:03:47

at least be very careful about this

thom09:03:51

I mean, the canonical answer would be that with-redefs in a test is a code smell.

thom09:03:17

Especially if you’re hoping that it travels back in time to before the test is even executed.

thom09:03:33

But I understand life happens.

vlaaad09:03:52

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

👍 4
kwladyka09:03:05

so to correct myself: I understand how this happen. The solution is not to use partial when loading ns, so for example in def

thom09:03:24

That sounds like you’d get a chance to change the binding before it’s used yeah.

thom09:03:18

You could also be clever about how and when you load namespaces within your tests, perhaps.

kwladyka09:03:33

I got a wrong impression on the beginning why this doesn’t work. So got wrong conclusions.

mike_ananev11:03:47

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.

kwladyka12:03:20

Just do not use alpha 😉

borkdude12:03:03

attitude like that will stall progress in the ecosystem.

☝️ 8
borkdude12:03:52

@mike1452 could it be that you have some AOT-ed classes around? did you try lein clean, rm -rf target, etc?

pinkfrog12:03:55

is the approach of prewalk+eval common?

borkdude12:03:16

eval isn't that common in general, most people use macros

pinkfrog12:03:27

(w/prewalk-replace dm 
           (w/prewalk-replace om patterns)))
       (filter #(= (eval %) 24))

mike_ananev12:03:39

@borkdude Thanks!! It works now. clean target folder is a solution.

Varenya12:03:08

I keep running into issues where in the component reset stops working if i am running a figwheel for the client

Varenya12:03:16

has anyone else exp this before?

Varenya12:03:38

basically i get classnotfound error i have restart the repl to make it normal again

Varenya12:03:52

does figwheel also generate aot jars?

Varenya12:03:10

which messes with the namespace reloading?

Ramon Rios13:03:13

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

dchelimsky13:03:32

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?

Ramon Rios13:03:40

I think so. Let me read this. Thanks 🙂

Old account13:03:09

Is this lib abandoned https://github.com/ptaoussanis/timbre ? do you know what happened to the author?

dominicm13:03:17

Not abandoned

Old account14:03:00

looks like nothing happening...

dominicm14:03:24

What else is needed?

Old account14:03:39

PRs and issues to be worked on. People does contribute but nothing is merged

kelveden14:03:55

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

kelveden14:03:03

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.

kelveden14:03:33

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.

jba13:03:42

Why would it be abandoned? The last update was 3 days ago

dominicm14:03:01

The last update includes comments on issues. Not the same thing

manutter5114:03:10

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.

dominicm14:03:56

Clojure is pretty extensible in user space. So most libraries don't need to accept new things.

😞 4
mkvlr15:03:10

is it known and intended that transit cannot encode keys with metadata? See https://nextjournal.com/mk/transit-metadata-keys for a minimum repro.

p-himik15:03:26

The page is not publicly available.

mkvlr15:03:01

ugh, fixed, thanks!

p-himik15:03:22

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.

mkvlr15:03:57

it also works in transit-cljs so I think it’s unintended

mkvlr15:03:06

will open an issue

p-himik15:03:17

If something works somewhere, it doesn't mean that it should work. :)

p-himik15:03:58

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.

mkvlr15:03:46

well I believe transit is supposed to be able to encode clojure data structures

p-himik15:03:55

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.

mkvlr15:03:53

what do you mean with metadata as the key?

p-himik15:03:12

> a map with metadata as a key

mkvlr15:03:42

hmm, can metadata be a key? Doesn’t it have to be on something?

p-himik15:03:18

A map with metadata. :)

mkvlr15:03:29

oh sure, that works

mkvlr15:03:39

we are using that

p-himik15:03:08

So it's a problem with just symbols with metadata used as keys then. A bit narrower.

mkvlr15:03:34

oh, you mean a map as key in a map… let me try

p-himik15:03:48

Ooh, interesting - I can write it but it doesn't read it back.

mkvlr16:03:10

you need to pass the meta transform function

p-himik16:03:58

For writer - yes, I do that. But for reader?

p-himik16:03:46

Ah, my bad - I was writing to the same buffer multiple times. It is read back, with the metadata.

mkvlr16:03:54

seems like transit doesn’t use a cmap for the symbol with meta case

mkvlr16:03:17

if I add a second key {} to the map it works

mkvlr16:03:36

(write w {(with-meta 'key {:foo 'bar}) 'val {} 'val2})

mkvlr16:03:53

thanks for your help looking into this!

zilti16:03:51

Transit isn't meant to encode Clojure data though. It is meant to encode EDN. And EDN doesn't know metadata.

p-himik16:03:15

Transit doesn't have to do anything with EDN. Those are completely different formats.

p-himik16:03:51

But to be fair, it also doesn't have to do anything with Clojure - there are many implementations for different languages.

mkvlr17:03:38

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.

p-himik17:03:26

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.

mkvlr17:03:10

yes, it was added for Clojure metadata in transit-clj and it’s using a tagged array as a base type.

p-himik17:03:33

My point is that Transit is not about Clojure, that's all.

lilactown16:03:21

IMO this belongs in #off-topic, not #clojure

niveauverleih16:03:15

Agree. I can't move it though.

p-himik16:03:59

So on that plot, what's on the Y axis?

p-himik16:03:12

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.

hindol16:03:59

it requires higher CPU utilization (which drives up hiring and operating costs) - what does this even mean?

p-himik16:03:33

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. :)

😂 8
hindol16:03:47

I am more confused by the higher CPU utilization bit.

hindol16:03:06

And how it affects the two costs.

p-himik16:03:36

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.

niveauverleih16:03:07

Hiring costs refers to steep learning curve.

niveauverleih16:03:46

Y-axis seems to be number of job ads.

p-himik16:03:12

> 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.

niveauverleih16:03:28

These might be mere perceptions or tentative, and wrong, explanations. Y-axis could be normalised.

p-himik16:03:59

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

ghadi16:03:51

garbage article

💯 4
markbastian18:03:53

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.

Alex Miller (Clojure team)16:03:41

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

kelveden16:03:15

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?

Alex Miller (Clojure team)16:03:43

nothing I can easily share

Alex Miller (Clojure team)16:03:39

unique ips downloading clojure artifact over the last year

4
markbastian18:03:10

It's going up where I work!

Alex Miller (Clojure team)16:03:57

so, apply grains of salt appropriately

lukasz16:03:53

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).

dharrigan16:03:35

Is there a way to find out what has changed between clojure-tools 1.10.1.507 and clojure-tools 1.10.1.536?

dharrigan16:03:51

which ultimately calls "

dchelimsky16:03:27

Got it, sort of. Are you looking for changes in clojure between those jars or the diff in tools.deps.alpha?

dchelimsky16:03:34

No easy answer in either case, but that'll help me help you get your answer.

dharrigan16:03:01

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.

dharrigan16:03:25

The latest on arch is 1.10.1-507.

dharrigan16:03:53

I understand that 507 and 536 is the number of commits

dharrigan16:03:56

but from where? 🙂

dchelimsky16:03:15

In cognitect-labs repos that's based on something documented in the repo, but I don't see anything the clojure repo.

dchelimsky16:03:28

Still looking

dharrigan16:03:04

thanks alex 🙂

dchelimsky16:03:16

@U11EL3P9U that give you the detail you need?

Alex Miller (Clojure team)16:03:22

if there's someplace you looked that that link could have existed, let me know so I can update

dchelimsky16:03:33

@U064X3EF3 part of the challenge is tying the clojure version to the tools.deps.alpha version

dchelimsky16:03:42

or vice versa, rather

dharrigan16:03:51

In the arch repo, it downloads from here source=(""

dharrigan16:03:08

so I went a-hunting to try and find out where I could find out information on that

dharrigan16:03:19

I naturally googled clojure tools

p-himik16:03:40

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.

Alex Miller (Clojure team)16:03:53

well, it's just all released together, windows too

dharrigan16:03:59

but that ended up at tools.deps.alpha, but with the changelog at a different version number

dharrigan16:03:05

so I thought that couldn't be it

Alex Miller (Clojure team)16:03:15

gotcha, I'll see if I can add some links in a few more places

dharrigan16:03:50

: 0.8.677 != 1.10.1.536

dharrigan16:03:56

So then I went to the clojure github

Alex Miller (Clojure team)16:03:01

the brew-install repo is poorly named but I've been too lazy to change it and fix all the build automation

dharrigan16:03:03

but that totally didn't fit

dharrigan16:03:17

so I got lost where to find out

p-himik16:03:43

The second face of "naming is hard" - you also have to change all the references if you change the name.

dharrigan16:03:12

but now I know where it links the homebrew install script (called clojure-tools!) (references (indirectly) to clojure.tools

dharrigan16:03:26

(not that I use homebrew - I'm 100% arch :))

dharrigan16:03:07

I can raise an outdated on arch now, since 0.8.677 (which is in 1.10.1.536) contains some fixes

colinkahn17:03:08

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))))

Alex Miller (Clojure team)17:03:18

I would probably not write the second one and instead just assoc using the first one

otwieracz17:03:18

Do I remember correctly that there was a function for turning sequence [:a :b :c] into map {0 :a 1 :b 2 :c} ?

jumpnbrownweasel17:03:18

(into {} (map-indexed vector [:a :b :c]))
=> {0 :a, 1 :b, 2 :c}

colinkahn17:03:01

You could use (zipmap (range) [:a :b :c])

ben17:03:06

user=> (zipmap (range) [:a :b :c])
{0 :a, 1 :b, 2 :c}
Ah well, I tried 😛

otwieracz17:03:41

Oh, probably zipmap was what I was looking for

hindol17:03:22

map-indexed with into also works but probably zipmap is better.

jumpnbrownweasel17:03:34

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.

colinkahn17:03:14

I guess more specifically my case involves threading a map through a few functions where prior functions add things that later functions use.

denis_krivosheev18:03:03

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>?

hiredman18:03:09

java does type erasure for generics

hiredman18:03:28

so at the jvm level there is no such thing as Iterable<Item>, just Iterable

denis_krivosheev18:03:30

So I can just extend Iterable, right?

denis_krivosheev18:03:59

Great! Thank you very much

denis_krivosheev18:03:58

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?

andy.fingerhut18:03:54

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"

denis_krivosheev18:03:27

@U0CMVHBL2 something is better than nothing. Could you please point out to example

andy.fingerhut18:03:31

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.

noisesmith18:03:33

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

andy.fingerhut18:03:17

Note: Depending on what you want to do, implementing a new data type may be more work than you need to go to.

andy.fingerhut18:03:33

Not many Clojure developers do that.

noisesmith18:03:17

there is of course defrecord which just does the right thing for the use case of "a hash map with behaviors"

denis_krivosheev18:03:52

This just a learning thing. I want to work out Sedgewick’s “Algorithms” book, but with Clojure

hindol18:03:03

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

hindol18:03:55

There is a nice little scaffold generator linked in the comments (not mine).

markbastian18:03:46

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.

hiredman18:03:15

I don't know of any documentation like that

ghadi18:03:36

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

denis_krivosheev18:03:38

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)

ghadi18:03:03

oh -- that's good to know your use case

ghadi18:03:37

look at something like that

bfabry18:03:46

working from a traditional algorithms book where mutability is assumed this is going to be fun in clj 🙂

ghadi18:03:54

that one is not simple -- it uses a lot of interfaces 🙂

denis_krivosheev18:03:03

Yes, a lot of fun 😂

andy.fingerhut18:03:08

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

ghadi18:03:16

Same with seqs

ghadi18:03:36

(mapv #(Item. %) collection) <- can pass that as Iterable<Item> (I don't know what Item is, assuming it's just for the example)

Braden Shepherdson19:03:10

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?

Alex Miller (Clojure team)19:03:46

There is no way to answer that

Alex Miller (Clojure team)19:03:46

Some third party could add an extension at any time and that is stored in the protocol, not in the object

didibus20:03:43

TIL that function return type hints can be on the arg vector or on the var

didibus20:03:00

But anyone knows which has precedence?

didibus20:03:30

And if Fn is overloaded to multiple arities all returning the same type, can the var hint be used for all of them?

bfabry20:03:28

on the var "happens to work" iirc. documentation says the vector https://clojure.org/reference/java_interop#typehints

didibus21:03:24

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.

bfabry21:03:35

arglist wins

bfabry21:03:37

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

bfabry21:03:34

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

didibus21:03:05

@bfabry Cool! Thanks for checking that

didibus21:03:29

Seems arglist is still the more "correct" place then, but var also works.

Alex Miller (Clojure team)21:03:34

yes, arglist is the preferred place

didibus21:03:22

Good to know! For pure curiosity, any known history as to why it evolved to have both?

Alex Miller (Clojure team)22:03:51

separate design decisions at different times, I don't know the details

didibus22:03:40

Alright, thanks!

Alex Miller (Clojure team)21:03:06

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.

8
vlaaad22:03:52

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!

40
grounded_sage23:03:37

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"}

bfabry23:03:14

user=> (set/rename-keys m (zipmap (keys m) (map #(keyword (name %)) (keys m))))
{:a "a"}

bfabry23:03:27

user=> m
#:user{:a "a"}

hiredman23:03:41

if you are traversing the map, just traverse it once

bfabry23:03:58

that works too

hiredman23:03:02

traversing it twice to build the map to pass rename-keys, only to have rename keys traverse it again

grounded_sage23:03:32

@U0NCTKEV8 what would traversing once look like? So I can compare with what @bfabry shared.

bfabry23:03:26

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))

👍 8
grounded_sage23:03:55

I’m working with a couple million maps. So the faster one makes sense :P

bfabry23:03:07

and even that's slower than

(into {} (map (fn [[k v]] [(keyword (name k)) v])) m)

👍 4
hiredman23:03:38

better even to not have to remove the namespace

hiredman23:03:36

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

grounded_sage23:03:20

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.

hiredman23:03:04

writing csvs you shouldn't have to care about the keys

bfabry23:03:20

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

grounded_sage23:03:45

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.

hiredman23:03:06

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

hiredman23:03:01

so for any N of maps, the column munging should happen once

grounded_sage23:03:22

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)))

grounded_sage23:03:11

But I’m okay with this for now. My concern was that maps are not ordered? Or is this just sets?

hiredman23:03:30

maps are not ordered

grounded_sage23:03:20

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.

hiredman23:03:21

but it is very likely (not guaranteed) that maps that contain the same keys will be iterated in the same order

grounded_sage23:03:21

I mean my generate CSV function kind of depends on that behaviour of it being consistent.

bfabry23:03:45

store the first set of keys, do (map (fn [row] (map #(% row) header-keys)) data) instead

hiredman23:03:05

you can just map row

grounded_sage23:03:07

@bfabry that looks more like going from CSV to maps?

bfabry23:03:08

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

grounded_sage23:03:51

Hmm must be time for bed for me then haha.

bfabry23:03:36

it's clearer broken down

(map (fn [map-row]
  (map #(get map-row %) header-keys))

👍 4
bfabry23:03:52

well, reformatted

bfabry23:03:34

and missing the last parameter :face_with_rolling_eyes: but hopefully you get me

grounded_sage23:03:09

I’m pretty tired. I will have to throw some sample data at it in the repl.

grounded_sage23:03:53

So simple and I don’t get it. Definitely tired haha.

grounded_sage23:03:27

I’m not convinced that code actually works lol

grounded_sage23:03:38

Oh I see. It’s a seq of maps being passed in.

grounded_sage23:03:58

So you destructure a seq of maps using a vector of header keys which ensures they are placed in the right order.

bfabry23:03:26

(let [header-keys (keys (first [m]))]
  (map (fn [map-row]
  (map #(get map-row %) header-keys)) [m]))
(("a"))

👍 4
bfabry23:03:33

replace [m] with data

kwladyka09:03:34

(-> (clojure.walk/stringify-keys m)
      (clojure.walk/keywordize-keys))

❤️ 4