Fork me on GitHub
#beginners
<
2021-02-19
>
Zak Singh00:02:01

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?

hiredman01:02:35

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

Zak Singh02:02:15

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

hiredman01:02:05

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

Todd01:02:43

High…noob here

dpsutton01:02:02

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

Todd01:02:20

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

Todd01:02:07

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

🎉 4
AC01:02:10

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

Todd01:02:06

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

Todd01:02:07

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.

👍 3
Todd01:02:42

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

seancorfield02:02:09

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

😀 4
Todd02:02:57

Thanks! Pretty excited.

seancorfield02:02:42

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

seancorfield02:02:01

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

Todd02:02:33

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

Todd02:02:05

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

Todd02:02:52

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

seancorfield02:02:04

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

Todd02:02:08

i LOVE Spock (which is written in Groovy)

Todd02:02:55

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

Thomas Tay02:02:55

Congrats on the job Todd!

Todd02:02:05

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

Todd02:02:25

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

Todd02:02:05

pretty scared but pretty psyched in same breath

seancorfield02:02:53

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

Todd02:02:26

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

Todd02:02:31

for a while

Todd02:02:39

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

Thomas Tay02:02:57

@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

Todd02:02:24

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

Todd02:02:08

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

Todd02:02:15

it was pretty cool

Todd02:02:44

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

Todd02:02:54

but didn’t ever judge me

Todd02:02:53

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

seancorfield02:02:32

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

Todd02:02:15

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

seancorfield02:02:49

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

Todd02:02:15

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

seancorfield02:02:12

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.

Todd02:02:53

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

Todd02:02:28

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

seancorfield02:02:40

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 🙂

Todd02:02:26

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

seancorfield02:02:18

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

Todd02:02:54

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

Todd03:02:23

saw some pretty amazing stuff done with it

Todd03:02:10

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

Yang Xu11:02:06

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

pavlosmelissinos12:02:14

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

Yang Xu12:02:35

I have some data like this: [ "a" {"b" "c"} "a2" {"b" "c"}] a vector that contains map, and I want to convert nested map to vector. My expect result like this: [ "a" ["b" "c"] "a2" ["b" "c"]] , convert all nested maps to vector

pavlosmelissinos12:02:19

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

Yang Xu12:02:35

I have some data like this: [ "a" {"b" "c"} "a2" {"b" "c"}] a vector that contains map, and I want to convert nested map to vector. My expect result like this: [ "a" ["b" "c"] "a2" ["b" "c"]] , convert all nested maps to vector

Adrian Imanuel12:02:54

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

Adrian Imanuel12:02:13

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.

borkdude12:02:57

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

Adrian Imanuel12:02:10

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

Marco Pas13:02:52

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

dharrigan13:02:20

Are you able to set the "Accept"?

dharrigan13:02:38

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

dharrigan13:02:51

Accept: "application/json" may help?

dharrigan13:02:29

(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 Pas13:02:24

@dharrigan i was using httpie with the same result 😞

dharrigan13:02:46

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

Marco Pas13:02:04

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

dharrigan13:02:23

oh wait, I misread, you're the server

dharrigan13:02:51

what happens if you do this?

Marco Pas13:02:33

I am trying to implement the server indeed 🙂

Marco Pas13:02:45

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

dharrigan13:02:19

(def app
  (-> (wrap-json-response app-routes)))

(comment

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

 (.stop server)

 ,)

dharrigan13:02:26

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

dharrigan13:02:32

So does this

dharrigan13:02:34

(def app
  (-> (wrap-json-response app-routes)
      (wrap-json-body)))

dharrigan14:02:16

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

Todd14:02:57

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?

Alex Miller (Clojure team)14:02:39

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

Alex Miller (Clojure team)14:02:58

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

Alex Miller (Clojure team)14:02:07

and maybe the London Clojure user group?

borkdude14:02:30

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

Todd14:02:19

Ok thanks @alexmiller… aw shucks!

borkdude14:02:07

Check the #events channel

Todd14:02:12

@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

borkdude14:02:49

Yes, everyone is welcome

agile_geek16:02:28

Same in the London Clojurians meetups

agile_geek16:02:42

You just missed a great talk by @U0L91U7A8

Alex Miller (Clojure team)14:02:15

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

Alex Miller (Clojure team)14:02:42

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

borkdude14:02:07

I love TBD! Those are the best talks

simongray14:02:37

To Better Do is the killer app Clojure deserves.

Todd14:02:17

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

Todd14:02:57

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

Todd14:02:50

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

Todd14:02:28

twcrone on twitter

aratare14:02:28

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.

Todd14:02:24

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

borkdude14:02:53

@rextruong Probably best to ask in #honeysql

aratare14:02:03

Will do. Thanks.

Todd15:02:25

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.

Todd15:02:20

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.

Alex Miller (Clojure team)15:02:59

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

Todd15:02:36

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

Todd15:02:53

Do they both still apply to modern Clojure.

Alex Miller (Clojure team)15:02:41

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.

Alex Miller (Clojure team)15:02:55

Clojure Applied 2nd ed is in ... consideration

🙏 6
Todd15:02:53

is it beta on prag prog yet?

grazfather15:02:12

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

grazfather15:02:54

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]
  (-> (format api-current-weather manhattan-city-id api-key)
      slurp
      json/read-str
      (get-in ["main" "temp"])
      float))

borkdude16:02:05

you should probably use let here and do appropriate checks

borkdude16:02:16

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

borkdude16:02:41

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

borkdude16:02:41

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

grazfather20:02:14

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

grazfather20:02:39

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

borkdude20:02:38

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

borkdude20:02:56

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

grazfather20:02:06

so what do you use as a web client?

borkdude20:02:31

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

borkdude20:02:55

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

grazfather20:02:41

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

borkdude20:02:25

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

grazfather20:02:03

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

Eamonn Sullivan17:02:04

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> (import '[java.nio.file Files])
java.nio.file.Files
user> (Files/getAttribute ( "/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

hiredman17:02:40

you need to pass an empty array of link options

hiredman17:02:22

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

andy.fingerhut17:02:24

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

hiredman17:02:56

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

Eamonn Sullivan18:02:27

Thanks all!

user> (import '[java.nio.file Files LinkOption])
java.nio.file.LinkOption
user> (require '[babashka.fs :as fs])
nil
user> (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.

Todd18:02:55

This channel is GREAT btw…

borkdude18:02:03

@eamonn.sullivan

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

Eamonn Sullivan18:02:10

Ah, that's even easier.

Souki19:02:31

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

dpsutton19:02:57

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

3
noisesmith19:02:12

complement does work here

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

3
borkdude19:02:05

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

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

3
dpsutton19:02:15

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

3
Souki19:02:27

thank you all for you answers

borkdude19:02:51

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

borkdude19:02:08

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

thumbnail20:02:39

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

👀 3
andy.fingerhut20:02:24

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

andy.fingerhut20:02:36

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

thumbnail20:02:52

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

andy.fingerhut20:02:14

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 🙂

andy.fingerhut20:02:36

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

thumbnail20:02:41

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.

thumbnail20:02:28

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

andy.fingerhut20:02:48

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})
    => {:x [1 2 3], :y [2], :z [4]}"
  [f & 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))))

❤️ 3
thumbnail20:02:45

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

andy.fingerhut20:02:14

Doh! Good catch.

borkdude22:02:48

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)

borkdude22:02:11

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

borkdude22:02:49

then you could just use merge-with conj

borkdude22:02:47

(merge-with conj
            {:x []}
            {:x 1}
            {:x 1}
            {:x 1})

andy.fingerhut22:02:42

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.

thumbnail16:02:34

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.