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?It is
I am not sure you get fine grained control of how deep it goes
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
High…noob here
Hey glad you made it. Sorry about the invite link issues.
haha…no worries, I was scared I was just THAT bad
just accepted my first job where I should actually get to write Clojure, so pretty psyched
congratulations! “should actually get”.. is there doubt about you using clojure?
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
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.
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
got it working - turns out there’s actually a binding for recursion depth: https://clojuredocs.org/clojure.spec.alpha/*recursion-limit*
That sounds pretty encouraging @twcrone Congrats on the new gig!
Thanks! Pretty excited.
Given your background in Java, Groovy, and Kotlin, I can't resist asking: which do you prefer?
(and of course soon you'll prefer Clojure to all of those @twcrone 🙂 )
of the 3…I can soundly say Java is my LEAST favorite
I really like Groovy for its flexibility but I like the functional parts of Kotlin
I did Groovy alot more than Kotlin but used Kotlin in high volume work at Kroger
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").
i LOVE Spock (which is written in Groovy)
I think I’d do Kotlin for my main code and test it with Groovy/Spock
Congrats on the job Todd!
Did Kotlin for a couple years at Kroger now doing Groovy again. Having immutability by default and some other Kotlin stuff is pretty nice
Thanks!
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
pretty scared but pretty psyched in same breath
Clojure is certainly "very different" but it is the most fun I've ever had with a language in... 35+ years of professional programming...
NICE
yeah, even the little homework project I did for GR was some of the most fun I’ve had
for a while
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
@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
sure
as far as I understand, they have a strong mentorship culture
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
it was pretty cool
he said my code was readable and well structured but he could tell I was new to Clojure
but didn’t ever judge me
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
When we switched our tech to Clojure, we found we got a lot of folks interested in learning Clojure applying 🙂
yeah, Kroger was okay me learning Kotlin with them cause they needed devs bad
I seem to remember Kroger was quite a heavy ColdFusion user back in the day...
haha…yeah my old scrum master had often joked of his ColdFusion days
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.
ah…I did Adobe Flex for a few years before it basically died but never CF
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
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 🙂
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
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).
Yeah, it was fun for a non-UI dev to do some pretty cool stuff pretty easily
saw some pretty amazing stuff done with it
Anyways gonna build a Clojure web app with Clojure on the backend and then port my three.js to ClojureScript if possible
Hi, How to update all values in the nested map? I tried update-in, but it didn't work.
can you provide an example of what you're trying to do?
wow, i wish i could land a job by learning clojure here... still looking hahahah
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.
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@adrianimanuel Keep an eye on #jobs and #remote-jobs
oh waow... didn't know there's that... Thanks a lot @borkdude
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?Are you able to set the "Accept"?
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
Accept: "application/json"
may help?
(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))
@dharrigan i was using httpie with the same result 😞
so your server doesn't know how to send back data as "Content-Type: application/json"
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"}
oh wait, I misread, you're the server
what happens if you do this?
I am trying to implement the server indeed 🙂
Let me try
I guess it has something to do with the middleware order.. at least that is my feel
Indeed
This works
(def app
(-> (wrap-json-response app-routes)))
(comment
(def server (jetty/run-jetty app {:port 3000 :join? false}))
(.stop server)
,)
❯ 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"
}
So does this
(def app
(-> (wrap-json-response app-routes)
(wrap-json-body)))
.
btw there is also the #ring channel. You may find additional help there 🙂
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?
(just fyi, most Cognitect devs are remote and don't actually live near the office)
I think the people meeting most frequently online are the sci clojure folks
and maybe the London Clojure user group?
The Dutch Clojure Meetup just had an online meetup Wednesday (with @ericdallo on clojure-lsp!). We meet every month.
Ok thanks @alexmiller… aw shucks!
Check the #events channel
@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
but you are fortunate in that that is the most likely place for the yearly Clojure conj conference to happen
YES!
we didn't hold it last year but it was in Durham in 2019 and 2018
this year still TBD
Yes, everyone is welcome
I love TBD! Those are the best talks
:)
I moved out here to be closer to interesting devs…I’m originally from Lexington, KY. Not much going on around there.
I didn’t expect to get a job doing Clojure!
welcome :)
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
twcrone on twitter
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.
no worries @rextruong… in person I’m a rambler…here…well…
To Better Do is the killer app Clojure deserves.
@rextruong Probably best to ask in #honeysql
Will do. Thanks.
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.
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.
Clojure Applied has a little bit of this, and Joy of Clojure has some as well
Ok, I read a little of both some time ago. Memory serves that Clojure Applied author doesn’t know what he’s talking about.
true dat
Do they both still apply to modern Clojure.
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.
Clojure Applied 2nd ed is in ... consideration
is it beta on prag prog yet?
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
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))
Same in the London Clojurians meetups
You just missed a great talk by @viebel
you should probably use let
here and do appropriate checks
e.g. (.exists (io/file ...))
before slurp
oh I see, you are using slurp as an http client
maybe don't use slurp as an http client then ;)
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 (<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
you need to pass an empty array of link options
Ah.
the getAttribute method in java takes a variable number of LinkOption arguments at the end
Java interop from Clojure exposes (i.e. does not attempt to hide) the implementation detail that Java varargs happen by passing Java arrays
which at the jvm level means the method takes an array of LinkOption arguments
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.This channel is GREAT btw…
Ah, that's even easier.
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
I’d probably just use a not contains? But either would get the job done
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 wildI just looked through our code-base and saw this:
(take-while (complement #{\>}))
yeah, that works ;)Ah. I use complement so infrequently I wasn’t sure how it would handle multiple args
thank you all for you answers
So I guess you could write (not (contains? {:a 0} :a))
as ((complement :a) {:a 0})
if you want to be fancy
(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)
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
I will look into a real HTTP client, though 🙂 I just sort of would like a primer on clojure best practices
Clojure has a mixed approach on this. Sometimes nil
is returned, sometimes an exception, sometimes people use {:val :foo}
or {:error :bar}
And you have various libs for this too (I use none of them personally)
so what do you use as a web client?
Depends. clj-http is a really popular one, httpkit as well (this is available in babashka as well)
There are various others. Nowadays Java 11 comes with an async client as well, for which there are various clj wrappers
(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?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.
https://borkdude.github.io/re-find.web/?args=%7B%3Ax%201%7D%20%7B%3Ax%201%7D&ret=%7B%3Ax%20%5B1%201%5D%7D Couldn't find a(nother) clojure.core fn 😬
Cool, I will play around with it. Thank you!
If you are in babashka, babashka.curl
is also an option
Note that if a key appears only once in the input maps, its value won't be put into a vector.
Not sure what inputs you are trying to handle there, though.
In this case I am not 🙂 I have a long-running process that apparently crashes when I accidentally unplug my router 🙂
> its value won't be put into a vector. This is a good point 😮.
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 🙂
Or alternately, called f
with 0 args just before combining it with a value of a newly encountered key.
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.
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
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))))
That's awesome! conj
' 0 arity already returns a vec; so f
can basically be conj
in your example. That's great
Doh! Good catch.
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)
{:x []}
That would simplify this logic a bunchthen you could just use merge-with conj
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.
true that