beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Zak Singh 2021-02-19T00:55:01.350200Z

Can spec be used to generate recursive-y data structures? I have a type coming from an API like this:

{ :kind 'NON_NULL',
      :name nil,
      :ofType {:kind 'LIST',
               :name nil,
               :ofType {:kind 'NON_NULL',
                        :name nil,
                        :ofType {:kind 'OBJECT', :name 'User'}}}}
I have a function which recurses through the :ofType fields until it reaches a level in which :name is not nil. I just read about spec and thought this may be an interesting scenario - the goal would be for it to generate these sequences up to a certain depth - say 5 layers deep. Is such a thing possible with spec?

2021-02-19T01:05:16.350400Z

It is

2021-02-19T01:05:35.351Z

I am not sure you get fine grained control of how deep it goes

2021-02-19T01:07:05.352800Z

I believe the way the underlaying test.check library (which is what is used for generating) works is the more examples you generate the "larger" examples it will genrate

Todd 2021-02-19T01:45:43.353400Z

High…noob here

dpsutton 2021-02-19T01:47:02.353900Z

Hey glad you made it. Sorry about the invite link issues.

Todd 2021-02-19T01:47:20.354200Z

haha…no worries, I was scared I was just THAT bad

Todd 2021-02-19T01:48:07.354900Z

just accepted my first job where I should actually get to write Clojure, so pretty psyched

🎉 1
AC 2021-02-19T01:49:10.355800Z

congratulations! “should actually get”.. is there doubt about you using clojure?

Todd 2021-02-19T01:50:06.356900Z

I’m pretty sure….but they have started to use Typescript on UI…not sure where I will end up…although i’m currently nearly 100% backend JVM dev with Java, Groovy and Kotlin

Todd 2021-02-19T01:51:07.357900Z

I accepted position with Guaranteed Rate and they were ok I had no practical Clojure experience but I am anxious to learn and use Clojure.

👍 1
Todd 2021-02-19T01:52:42.359Z

In my tech screening the tech lead helped me modify my “homework” and knew I was a Clojure noob but was very nice about it and not judgey…that was pretty cool

Zak Singh 2021-02-19T02:17:15.359300Z

got it working - turns out there’s actually a binding for recursion depth: https://clojuredocs.org/clojure.spec.alpha/*recursion-limit*

seancorfield 2021-02-19T02:35:09.359900Z

That sounds pretty encouraging @twcrone Congrats on the new gig!

😀 1
Todd 2021-02-19T02:35:57.360300Z

Thanks! Pretty excited.

seancorfield 2021-02-19T02:38:42.360900Z

Given your background in Java, Groovy, and Kotlin, I can't resist asking: which do you prefer?

seancorfield 2021-02-19T02:40:01.361300Z

(and of course soon you'll prefer Clojure to all of those @twcrone 🙂 )

Todd 2021-02-19T02:40:33.361700Z

of the 3…I can soundly say Java is my LEAST favorite

Todd 2021-02-19T02:41:05.362400Z

I really like Groovy for its flexibility but I like the functional parts of Kotlin

Todd 2021-02-19T02:41:52.363600Z

I did Groovy alot more than Kotlin but used Kotlin in high volume work at Kroger

seancorfield 2021-02-19T02:42:04.364200Z

Yeah, when I switched from Java to Groovy, I was very happy. I haven't used Kotlin in production but I learned it for fun and it seems really nice (as part of The Pragmatic Programmer's advice to "learn a new language every year").

Todd 2021-02-19T02:42:08.364300Z

i LOVE Spock (which is written in Groovy)

Todd 2021-02-19T02:42:55.365100Z

I think I’d do Kotlin for my main code and test it with Groovy/Spock

2021-02-19T02:43:55.366Z

Congrats on the job Todd!

Todd 2021-02-19T02:44:05.366300Z

Did Kotlin for a couple years at Kroger now doing Groovy again. Having immutability by default and some other Kotlin stuff is pretty nice

Todd 2021-02-19T02:44:10.366500Z

Thanks!

Todd 2021-02-19T02:45:25.367900Z

I think Clojure will give me the dynamic flexibility I have with Groovy with the functional parts of Kotlin plus Lisp…which is the part I’m less familiar with but I feel like if I can grok it, my life will change and I will think more betterly

Todd 2021-02-19T02:46:05.368300Z

pretty scared but pretty psyched in same breath

seancorfield 2021-02-19T02:46:53.369100Z

Clojure is certainly "very different" but it is the most fun I've ever had with a language in... 35+ years of professional programming...

Todd 2021-02-19T02:47:03.369500Z

NICE

Todd 2021-02-19T02:47:26.370Z

yeah, even the little homework project I did for GR was some of the most fun I’ve had

Todd 2021-02-19T02:47:31.370300Z

for a while

Todd 2021-02-19T02:48:39.372700Z

When I first started doing Groovy professionally it was AWESOME and exciting all the stuff I could do that never thought I could do. Clojure feels like that to me now

2021-02-19T02:48:57.373200Z

@twcrone I wonder how theyre going to train you in Clojure. Maybe a week in you could share your experiences? I don't use clojure professionally but am always curious how companies train programmers in new languages

Todd 2021-02-19T02:49:10.373400Z

sure

Todd 2021-02-19T02:49:24.373900Z

as far as I understand, they have a strong mentorship culture

Todd 2021-02-19T02:50:08.374800Z

like I said earlier, even during my tech assessment… the dev lead running it was coaching me on how I could improve my homework…not judging…mentoring me…in an interview

Todd 2021-02-19T02:50:15.375Z

it was pretty cool

Todd 2021-02-19T02:50:44.375700Z

he said my code was readable and well structured but he could tell I was new to Clojure

Todd 2021-02-19T02:50:54.376Z

but didn’t ever judge me

Todd 2021-02-19T02:51:53.377100Z

I think Guaranteed Rate is hiring a lot right now and plenty remote opportunities to devs interested in doing Clojure, might check them out…I’m not a recruiter

seancorfield 2021-02-19T02:52:32.377700Z

When we switched our tech to Clojure, we found we got a lot of folks interested in learning Clojure applying 🙂

Todd 2021-02-19T02:53:15.378400Z

yeah, Kroger was okay me learning Kotlin with them cause they needed devs bad

seancorfield 2021-02-19T02:53:49.378900Z

I seem to remember Kroger was quite a heavy ColdFusion user back in the day...

Todd 2021-02-19T02:54:15.379400Z

haha…yeah my old scrum master had often joked of his ColdFusion days

seancorfield 2021-02-19T02:55:12.380400Z

Where I work now still has CFML code (but I'm rewriting it all in Clojure). And I did a lot of CFML because I was working at Macromedia when they acquired Allaire.

Todd 2021-02-19T02:55:53.380900Z

ah…I did Adobe Flex for a few years before it basically died but never CF

Todd 2021-02-19T02:57:28.382700Z

I have a graphics “game” I wrote with JavaScript three.js and backend Kotlin that I’m going to try to rewrite with Clojure over the next couple weeks

seancorfield 2021-02-19T02:57:40.383Z

Ah, Flex! Yes, a startup I worked at for a while used Flex and Air to build a cool desktop collaboration platform -- backed by Groovy (that's where I got my Groovy experience) and CFML 🙂

Todd 2021-02-19T02:58:26.383900Z

don’t judge, but I kinda enjoyed Flex and went all in…I was never big into JS…but then it basically died and I went back to Groovy

seancorfield 2021-02-19T02:59:18.385Z

As far as proprietary systems go, Flex was pretty good (I know it was released to Apache as open source but it really had passed its prime by that point).

Todd 2021-02-19T02:59:54.385600Z

Yeah, it was fun for a non-UI dev to do some pretty cool stuff pretty easily

Todd 2021-02-19T03:00:23.386Z

saw some pretty amazing stuff done with it

Todd 2021-02-19T03:02:10.386700Z

Anyways gonna build a Clojure web app with Clojure on the backend and then port my three.js to ClojureScript if possible

Yang Xu 2021-02-19T11:59:06.388200Z

Hi, How to update all values in the nested map? I tried update-in, but it didn't work.

pavlosmelissinos 2021-02-19T12:02:14.389800Z

can you provide an example of what you're trying to do?

2021-02-19T12:08:54.390700Z

wow, i wish i could land a job by learning clojure here... still looking hahahah

2021-02-19T12:11:13.392300Z

for me that's only have experience in Excel & vba, learning clojure is pretty similar to how combination of function /formula in excel, with the immutability.

pavlosmelissinos 2021-02-19T12:45:19.392500Z

update-in follows a path in a nested associative structure and updates a single item (the item could be e.g. a map or a list of course but it should be a single thing) That's not what you want here There's probably a better way but you can use this to get the result you want:

(def coll ["a" {"b" "c"} "a2" {"b" "c"}])
(mapv #(if (map? %) (apply into [] %) %) coll)
However, if I were you, I'd go a step back and reconsider how the data is modeled. Why does coll look like that? It's a bit unusual: from the looks of it, it could be a map but it's a vector and it has some elements in it that are maps with a single key edit: modified incorrect information about update-in

borkdude 2021-02-19T12:47:57.393300Z

@adrianimanuel Keep an eye on #jobs and #remote-jobs

2021-02-19T12:49:10.393800Z

oh waow... didn't know there's that... Thanks a lot @borkdude

Marco Pas 2021-02-19T13:32:52.394600Z

I am trying to create my first ring/compojure application and seem to hit a wall. I am returning a simple JSON response and the app seems to always return with the incorrect content type.

(ns clojure-rest-server.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.util.response :refer [response]]
            [clojure.pprint]
            [ring.handler.dump :refer [handle-dump]]
            [ring.middleware.json :refer [wrap-json-response wrap-json-body]]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]))

(defroutes app-routes
  (POST "/" {body :body} (response {:msg "hello-world"}))
  (route/not-found "Not Found"))

(def app
  (-> (wrap-defaults app-routes api-defaults)
      (wrap-json-body)
      (wrap-json-response)))
When i do a curl or use postman i allways get a Content-Type: application/octet-stream
$ curl -i --header "Content-Type: application/json" \
  --request POST \
  <http://localhost:3000>

HTTP/1.1 200 OK
Date: Fri, 19 Feb 2021 13:30:50 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Server: Jetty(9.2.21.v20170120)

{"msg":"hello-world"}%
Any hint what i am doing wrong?

dharrigan 2021-02-19T13:35:20.394900Z

Are you able to set the "Accept"?

dharrigan 2021-02-19T13:35:38.395400Z

You're telling the server, correctly, that the content-type on the post is json, but you're not telling it what you'll accept in return

dharrigan 2021-02-19T13:35:51.395700Z

Accept: "application/json" may help?

dharrigan 2021-02-19T13:37:29.396900Z

(as an aside, I can recommend httpie as a great client that natively understands how to work with JSON data, instead of using curl (which is great of course, but httpie is also good for doing json stuff without the hassle))

Marco Pas 2021-02-19T13:38:24.397500Z

@dharrigan i was using httpie with the same result 😞

dharrigan 2021-02-19T13:38:46.398Z

so your server doesn't know how to send back data as "Content-Type: application/json"

Marco Pas 2021-02-19T13:39:04.398600Z

http POST <http://localhost:3000>                                                                                                             14:38:45
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Date: Fri, 19 Feb 2021 13:38:52 GMT
Server: Jetty(9.2.21.v20170120)
Transfer-Encoding: chunked

{"msg":"hello-world"}

dharrigan 2021-02-19T13:39:23.398900Z

oh wait, I misread, you're the server

dharrigan 2021-02-19T13:39:51.399100Z

what happens if you do this?

Marco Pas 2021-02-19T13:40:33.399700Z

I am trying to implement the server indeed 🙂

dharrigan 2021-02-19T13:41:51.400400Z

Let me try

Marco Pas 2021-02-19T13:47:45.401Z

I guess it has something to do with the middleware order.. at least that is my feel

dharrigan 2021-02-19T13:57:15.401400Z

Indeed

dharrigan 2021-02-19T13:57:17.401600Z

This works

dharrigan 2021-02-19T13:57:19.401800Z

(def app
  (-&gt; (wrap-json-response app-routes)))

(comment

 (def server (jetty/run-jetty app {:port 3000 :join? false}))

 (.stop server)

 ,)

dharrigan 2021-02-19T13:57:26.402Z

❯ http POST localhost:3000
HTTP/1.1 200 OK
Content-Length: 21
Content-Type: application/json;charset=utf-8
Date: Fri, 19 Feb 2021 13:56:59 GMT
Server: Jetty(9.4.36.v20210114)

{
    "msg": "hello-world"
}

dharrigan 2021-02-19T13:58:32.402200Z

So does this

dharrigan 2021-02-19T13:58:34.402400Z

(def app
  (-&gt; (wrap-json-response app-routes)
      (wrap-json-body)))

dharrigan 2021-02-19T13:58:35.402600Z

.

dharrigan 2021-02-19T14:00:16.403300Z

btw there is also the #ring channel. You may find additional help there 🙂

Todd 2021-02-19T14:09:57.404500Z

So Cognitect is near where I live and the local Clojure Users Group doesn’t appear to have been active sinch 2019. Any online Clojure users group that meetings virtually on a regular basis?

alexmiller 2021-02-19T14:24:39.407700Z

(just fyi, most Cognitect devs are remote and don't actually live near the office)

alexmiller 2021-02-19T14:24:58.408100Z

I think the people meeting most frequently online are the sci clojure folks

alexmiller 2021-02-19T14:25:07.408400Z

and maybe the London Clojure user group?

alexmiller 2021-02-19T14:25:40.408600Z

https://www.meetup.com/London-Clojurians/events/

alexmiller 2021-02-19T14:25:54.408900Z

https://scicloj.github.io/pages/web_meetings/

borkdude 2021-02-19T14:26:30.410Z

The Dutch Clojure Meetup just had an online meetup Wednesday (with @ericdallo on clojure-lsp!). We meet every month.

Todd 2021-02-19T14:27:19.410900Z

Ok thanks @alexmiller… aw shucks!

borkdude 2021-02-19T14:28:07.411900Z

Check the #events channel

Todd 2021-02-19T14:28:12.412200Z

@borkdude thanks! I’ll join if they’ll have me! I’d love to find some Clojure devs in the Triangle area to rub elbows with

alexmiller 2021-02-19T14:28:15.412400Z

but you are fortunate in that that is the most likely place for the yearly Clojure conj conference to happen

Todd 2021-02-19T14:28:24.412700Z

YES!

alexmiller 2021-02-19T14:28:42.413200Z

we didn't hold it last year but it was in Durham in 2019 and 2018

alexmiller 2021-02-19T14:28:49.413400Z

this year still TBD

borkdude 2021-02-19T14:28:49.413500Z

Yes, everyone is welcome

borkdude 2021-02-19T14:29:07.414400Z

I love TBD! Those are the best talks

alexmiller 2021-02-19T14:29:16.414700Z

:)

Todd 2021-02-19T14:29:17.414800Z

I moved out here to be closer to interesting devs…I’m originally from Lexington, KY. Not much going on around there.

Todd 2021-02-19T14:29:57.415400Z

I didn’t expect to get a job doing Clojure!

alexmiller 2021-02-19T14:30:23.415600Z

welcome :)

Todd 2021-02-19T14:30:50.416200Z

you probably don’t remember me but I’ve bugged you on and off over the years about Clojure but never had a chance to actually use it much

Todd 2021-02-19T14:31:28.416500Z

twcrone on twitter

aratare 2021-02-19T14:32:28.417300Z

Hi there. Sorry to bug into the middle of your conversation 😅 Quick question about using HoneySQL/next.jdbc: Is there a nice or recommended way to handle conversion between dashes and underscores in keywords? There's :allow-dashed-names true but that's just allowing HoneySQL to include dashes in the formatted string. Thanks in advance.

Todd 2021-02-19T14:33:24.418Z

no worries @rextruong… in person I’m a rambler…here…well…

simongray 2021-02-19T14:33:37.418100Z

To Better Do is the killer app Clojure deserves.

borkdude 2021-02-19T14:33:53.418500Z

@rextruong Probably best to ask in #honeysql

aratare 2021-02-19T14:34:03.418700Z

Will do. Thanks.

Todd 2021-02-19T15:25:25.420700Z

Currently reading through Russ Olsen’s “Getting Clojure” in the evenings to help me ramp up in addition to rewriting some personal projects and watching videos etc. The book is nice about warning on gotchas and why things are certain ways and how things are typically done. Its a couple years old but I’m enjoying the read regardless.

Todd 2021-02-19T15:27:20.422200Z

Is there a good book that talks about internals etc of Clojure? I thought I read something years ago that has some of that stuff but I forget. Less of a workshop but more a way to think functional in Clojure in modern way.

alexmiller 2021-02-19T15:29:59.422700Z

Clojure Applied has a little bit of this, and Joy of Clojure has some as well

Todd 2021-02-19T15:30:36.423700Z

Ok, I read a little of both some time ago. Memory serves that Clojure Applied author doesn’t know what he’s talking about.

alexmiller 2021-02-19T15:30:44.424Z

true dat

😀 1
Todd 2021-02-19T15:30:53.424300Z

Do they both still apply to modern Clojure.

alexmiller 2021-02-19T15:31:41.425400Z

pretty much. Clojure has the advantage of changing slowly, and in a largely additive way, so pretty much all of the books out there are not wrong but may miss some newer features.

alexmiller 2021-02-19T15:31:55.425700Z

Clojure Applied 2nd ed is in ... consideration

🙏 2
Todd 2021-02-19T15:32:53.426100Z

is it beta on prag prog yet?

grazfather 2021-02-19T15:43:12.427300Z

Can someone recommend me a good guide on error handling? I find clojure works great until things mess up, and I think just littering everything with try/catch would make things pretty ugly

grazfather 2021-02-19T15:44:54.427500Z

E.g., this is nice, but if I lose network connectivity slurp will fail. If the API returns something that doesn’t conform to the spec I am expecting, everything fails. What’s best practice here? an API getter that returns an empty, but spec conforming structure when slurp fails?

(defn get-current-temperature
  [city-id]
  (-&gt; (format api-current-weather manhattan-city-id api-key)
      slurp
      json/read-str
      (get-in ["main" "temp"])
      float))

agile_geek 2021-02-19T16:17:28.428Z

Same in the London Clojurians meetups

agile_geek 2021-02-19T16:18:05.428200Z

https://www.meetup.com/London-Clojurians/

agile_geek 2021-02-19T16:18:42.428500Z

You just missed a great talk by @viebel

borkdude 2021-02-19T16:41:05.429100Z

you should probably use let here and do appropriate checks

borkdude 2021-02-19T16:41:16.429300Z

e.g. (.exists (io/file ...)) before slurp

borkdude 2021-02-19T16:41:41.429500Z

oh I see, you are using slurp as an http client

borkdude 2021-02-19T16:42:41.429700Z

maybe don't use slurp as an http client then ;)

Eamonn Sullivan 2021-02-19T17:47:04.431Z

Hi, having an issue trying to call a static method. I thought I understood it (I use System/getenv all the time), but this isn't working. What simple thing am I missing?

user&gt; (import '[java.nio.file Files])
java.nio.file.Files
user&gt; (Files/getAttribute (<http://clojure.java.io/as-file|clojure.java.io/as-file> "/home/eamonn/temp/api-config.json") "unix:nlink")
Syntax error (IllegalArgumentException) compiling . at (*cider-repl git/fs:localhost:46803(clj)*:89:7).
No matching method getAttribute found taking 2 args for class java.nio.file.Files

2021-02-19T17:52:40.432200Z

you need to pass an empty array of link options

Eamonn Sullivan 2021-02-19T17:53:10.433Z

Ah.

2021-02-19T17:53:22.433500Z

the getAttribute method in java takes a variable number of LinkOption arguments at the end

2021-02-19T17:53:24.433700Z

Java interop from Clojure exposes (i.e. does not attempt to hide) the implementation detail that Java varargs happen by passing Java arrays

2021-02-19T17:53:56.434400Z

which at the jvm level means the method takes an array of LinkOption arguments

Eamonn Sullivan 2021-02-19T18:01:27.435200Z

Thanks all!

user&gt; (import '[java.nio.file Files LinkOption])
java.nio.file.LinkOption
user&gt; (require '[babashka.fs :as fs])
nil
user&gt; (Files/getAttribute (fs/path "/home/eamonn/temp/api-config.json") "unix:nlink" (make-array LinkOption 0))
2
Was just trying to see if I can verify that something is a hard link.

Todd 2021-02-19T18:01:55.435500Z

This channel is GREAT btw…

borkdude 2021-02-19T18:12:03.436Z

@eamonn.sullivan

$ bb -e '(fs/get-attribute "." "unix:nlink")'
48

Eamonn Sullivan 2021-02-19T18:15:10.436300Z

Ah, that's even easier.

Souki 2021-02-19T19:21:31.439500Z

hello, what would be the best way for checking that a collection doesn't contain a key, the opposite of contains? thinking of using complement so far

dpsutton 2021-02-19T19:23:57.440100Z

I’d probably just use a not contains? But either would get the job done

✅ 1
2021-02-19T19:26:12.440700Z

complement does work here

(ins)user=&gt; ((complement contains?) {:a 0} :a)
false
(cmd)user=&gt; ((complement contains?) {:b 0} :a)
true
but I don't see complement used much in the wild

✅ 1
borkdude 2021-02-19T19:28:05.441200Z

I just looked through our code-base and saw this:

(take-while (complement #{\&gt;}))
yeah, that works ;)

✅ 1
dpsutton 2021-02-19T19:30:15.441900Z

Ah. I use complement so infrequently I wasn’t sure how it would handle multiple args

✅ 1
Souki 2021-02-19T19:41:27.442300Z

thank you all for you answers

borkdude 2021-02-19T19:43:51.443900Z

So I guess you could write (not (contains? {:a 0} :a)) as ((complement :a) {:a 0}) if you want to be fancy

borkdude 2021-02-19T19:45:08.444500Z

(be aware the this could cause problems with keys that map to false or nil vals, so either way contains? seems like a good bet)

grazfather 2021-02-19T20:09:14.444700Z

it worked for days! 😄 I am also used to python style: Let errors happens and catch them, instead of hcecking first. Checking first isn’t perfect, since the file can be deleted between the time you check and when you open it

grazfather 2021-02-19T20:09:39.444900Z

I will look into a real HTTP client, though 🙂 I just sort of would like a primer on clojure best practices

borkdude 2021-02-19T20:10:38.445100Z

Clojure has a mixed approach on this. Sometimes nil is returned, sometimes an exception, sometimes people use {:val :foo} or {:error :bar}

borkdude 2021-02-19T20:10:56.445300Z

And you have various libs for this too (I use none of them personally)

grazfather 2021-02-19T20:11:06.445500Z

so what do you use as a web client?

borkdude 2021-02-19T20:11:31.445800Z

Depends. clj-http is a really popular one, httpkit as well (this is available in babashka as well)

borkdude 2021-02-19T20:11:55.446Z

There are various others. Nowadays Java 11 comes with an async client as well, for which there are various clj wrappers

2021-02-19T20:13:39.446900Z

(merge-with (fn [a b]
              (if (coll? a)
                (conj a b)
                [a b]))
            {:x 1}
            {:x 1}
            {:x 1})
=&gt; {:x [1 1 1]}
Is there a better way to do this?

👀 1
2021-02-20T16:39:34.022600Z

Sorry, missed these. Andy is right, the keyset is actually unknown and this is partway in a threadlast macro, so it's some work to get the initial map in there.

grazfather 2021-02-19T20:16:41.447400Z

Cool, I will play around with it. Thank you!

borkdude 2021-02-19T20:18:25.447600Z

If you are in babashka, babashka.curl is also an option

2021-02-19T20:20:24.447900Z

Note that if a key appears only once in the input maps, its value won't be put into a vector.

2021-02-19T20:20:36.448100Z

Not sure what inputs you are trying to handle there, though.

grazfather 2021-02-19T20:21:03.448300Z

In this case I am not 🙂 I have a long-running process that apparently crashes when I accidentally unplug my router 🙂

2021-02-19T20:23:52.448500Z

> its value won't be put into a vector. This is a good point 😮.

2021-02-19T20:24:14.448700Z

A small change to merge-with that called f with arity 2 when combining values for the same key, or called f with arity 1 when first encountering a value of a new key, would make this more regular. That variant of merge-with isn't in any library I know, but I think I almost wrote it once 🙂

2021-02-19T20:25:36.448900Z

Or alternately, called f with 0 args just before combining it with a value of a newly encountered key.

2021-02-19T20:25:41.449100Z

The input is always a list of some key and a async/chan. Later in the process in need to async/merge the chans under the same key. So they should be in a vector.

2021-02-19T20:26:28.449300Z

That sounds like a xf 😉 Not a bad idea. I think i'll simply (map-vals vector) over the collection before merge-with into. which'd probably clean this up

2021-02-19T20:32:48.449600Z

Here is a maybe-buggy, not-performance-optimized, terrible name and doc string variant of merge-with:

(defn merge-with2
  "Variant of clojure.core/merge-with that calls f with 0 arguments
  when first encountering a new key.  f should return an initial value
  in that case.  When a map `m` is processed with a key that has not been
  encountered before (which is all of the keys in the first map given),
  the value associated with the key `k` will be (f (f) (m k)).

  Example:

    (merge-with2 (fn
                   ([] [])
                   ([a b] (conj a b)))
                 {:x 1}
                 {:y 2 :x 2}
                 {:x 3 :z 4})
    =&gt; {:x [1 2 3], :y [2], :z [4]}"
  [f &amp; maps]
  (when (some identity maps)
    (let [merge-entry (fn [m e]
			(let [k (key e) v (val e)]
			  (if (contains? m k)
			    (assoc m k (f (get m k) v))
			    (assoc m k (f (f) v)))))
          merge2 (fn [m1 m2]
		   (reduce merge-entry (or m1 {}) (seq m2)))]
      (reduce merge2 {} maps))))

❤️ 1
2021-02-19T20:33:45.449900Z

That's awesome! conj' 0 arity already returns a vec; so f can basically be conj in your example. That's great

2021-02-19T20:35:14.450200Z

Doh! Good catch.

borkdude 2021-02-19T22:09:48.451800Z

You could maybe start with a good identity element that has the empty coll at each of the expected keys? (if you expect to work with a fixed number of keys)

borkdude 2021-02-19T22:10:11.452Z

{:x []}
That would simplify this logic a bunch

borkdude 2021-02-19T22:10:49.452200Z

then you could just use merge-with conj

2021-02-19T22:32:42.452700Z

That is fine for the case when you know the set of keys. If you want it to always create vectors for arbitrary keys in arbitrary number of maps, then you need to construct the first map to contain the union of the keys of all other maps.

borkdude 2021-02-19T22:37:33.452900Z

true that