beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Tāhā 2020-10-08T01:29:19.001900Z

When I eval (s/exercise-fn insert-cookie)` , it works, there are no errors. However, when I try to "fix" insert-cookie by changing :limit to ::limit at 38:7 , and try running exercise-fn again, I get this cryptic error:

; Error printing return value (NullPointerException) at java.util.Objects/requireNonNull (Objects.java:247).
; temporal
This is my code:

Tāhā 2020-10-08T02:05:46.003900Z

I just realised that the error lies not in exercise-fn but in my insert-cookie fn, there's something wrong with the (::limit options) cond branch body, but I cannot figure out what

seancorfield 2020-10-08T03:01:43.006Z

@tahaahmedrasheedpk Given that you are passing in unqualified keys and your function deals with unqualified keys, I think your ::cookie-options spec needs :opt-un instead of :opt

seancorfield 2020-10-08T03:03:13.007Z

Same with ::name-value-pair -- you're using :req which is for qualified keywords. You want :req-un.

Tāhā 2020-10-08T03:03:41.007700Z

I'm doing the opposite; I want to change the keywords in insert-cookie to their qualified variants

Tāhā 2020-10-08T03:03:48.008100Z

But when I try doing that I get the error

seancorfield 2020-10-08T03:04:17.008700Z

Show the code that actually errors then, so we can help (but I suspect I know what the error is).

seancorfield 2020-10-08T03:04:39.009200Z

When you change to ::limit you also need to change the destructuring on keys.

seancorfield 2020-10-08T03:06:04.009800Z

(let [{::keys [limit]} options] <-- so you get the :: version

Tāhā 2020-10-08T03:07:20.010900Z

Yep did that too

seancorfield 2020-10-08T03:07:46.011600Z

In you main ns, the :: auto-resolves to :main/ as a qualifier. Your destructuring can either be {::keys [limit]} as above or {:main/keys [limit]} or {:keys [main/limit]}

Tāhā 2020-10-08T03:07:48.011700Z

(defn insert-cookie
  [headers name value options]
  (-&gt;&gt;
   (cond-&gt; ()
     (::limit options) (conj (let [{:keys [::limit]} options]
                               (if (integer? limit)
                                 (str "Max-Age=" limit)
                                 (str "Expires=" (.format DateTimeFormatter/RFC_1123_DATE_TIME limit)))))
     (::domain options) (conj (str "Domain=" (::domain options)))
     (::path options) (conj (str "Path=" (::path options)))
     (::secure options) (conj "Secure")
     (::httpOnly options) (conj "HttpOnly")
     (::sameSite options) (conj (::sameSite options))
     true (conj (str name "=" value)))
   (clojure.string/join "; ")
   (hash-map :name "Set-Cookie" :value)
   (conj headers)))
Here's what I have rn

seancorfield 2020-10-08T03:07:58.011900Z

No, that's not what I posted.

seancorfield 2020-10-08T03:08:05.012200Z

{::keys [limit]}

Tāhā 2020-10-08T03:08:30.012700Z

They're equivalent (I think)

seancorfield 2020-10-08T03:09:53.013600Z

Yeah, actually that does work -- I've never used that syntax (in ten years!) so I had to check in the REPL.

sova-soars-the-sora 2020-10-08T03:11:15.013900Z

O.O interesting

Tāhā 2020-10-08T03:13:38.014200Z

I think I found the problem, just a sec

seancorfield 2020-10-08T03:17:21.015100Z

When you exercise it, you get tagged output from ::limit so it is not valid input to insert-cookie (but you're not showing us code that would do that).

seancorfield 2020-10-08T03:18:18.015900Z

So what you're passing to do .format would be [:n 550000] (or [:t #inst ...])

Tāhā 2020-10-08T03:50:25.017100Z

So, the issue was that the inst? generator produced java.util.Date's (instead of java.time.instant's), which my function couldn't handle

Tāhā 2020-10-08T03:50:54.017500Z

Here's the fixed code:

(defn insert-cookie
  [headers name value options]
  (-&gt;&gt;
   (cond-&gt; ()
     (::limit options) (conj (let [{:keys [::limit]} options]
                               (if (integer? limit)
                                 (str "Max-Age=" limit)
                                 (cond-&gt;&gt;
                                  limit
                                   (instance? java.util.Date limit) (.toInstant)
                                   true (.format (.withZone
                                                  DateTimeFormatter/RFC_1123_DATE_TIME
                                                  (ZoneId/of "GMT")))
                                   true (str "Expires=")))))
     (::domain options) (conj (str "Domain=" (::domain options)))
     (::path options) (conj (str "Path=" (::path options)))
     (::secure options) (conj "Secure")
     (::httpOnly options) (conj "HttpOnly")
     (::sameSite options) (conj (::sameSite options))
     true (conj (str name "=" value)))
   (clojure.string/join "; ")
   (hash-map ::name "Set-Cookie" ::value)
   (conj headers)))

seancorfield 2020-10-08T03:53:02.018800Z

Oh, wow, that was a subtle one... yeah, I hadn't even noticed that... Dates are hard 😞 I'd love everything to be Java Time but the reality is a nightmare mix of java.util.Date (and the java.sql.* variants) and Java Time and sometimes Joda Time 😞

practicalli-john 2020-10-08T07:24:26.020400Z

What are templates you consider useful for creating projects (CLI, Leiningen or Boot)? I am interested in highlighting useful templates for beginners and experience clojure developers alike in the books and videos I create for Practicalli. I typically use the basic templates, such as app and lib that come with the clj-new project. However for creating simple websites I use figwheel-main (https://clojurebridgelondon.github.io/ and http://Practical.li websites). I have found templates useful to learn from without specifically using them to create projects, such as the https://luminusweb.com/. Do you use https://clj-templates.com/ to find a template or perhaps an internet search? Do you create your own templates, e.g. define a common starting point for all projects for your team. Perhaps you take the https://www.juxt.land/edge/ approach of cloning a base project repository. Please reply as a thread so I can follow or post a comment at Clojureverse https://clojureverse.org/t/what-project-templates-do-you-find-useful/6652 Thank you

practicalli-john 2020-10-08T07:27:47.020600Z

Some templates I have found useful include https://github.com/bhauman/figwheel-template for building landing pages, eg. for ClojureBridge London and http://Practical.li https://github.com/metosin/compojure-api for quickly creating an API with Swagger and Schema (I havent found a Reitit equivalent of compojure-api yet)

2020-10-08T08:27:24.024400Z

Hi @tahaahmedrasheedpk - been scanning through here. re:java.time and libraries, not sure if anyone's mentioned https://github.com/henryw374/time-literals, which provides literals for java.time - e.g.

#time/instant "2018-07-25T07:10:05.861Z"
also, I think sticking to the java.time api (rather than a wrapper) is a good idea especially if you're just learning it, but IMO cljc.java-time gives a better experience of that https://github.com/henryw374/cljc.java-time#-but-i-just-develop-on-the-jvm-only

👍 1
Jim Newton 2020-10-08T09:12:47.026400Z

I worked for several hours trying to figure out why a very simple function was not returning the same thing within the program as I get at the repl. I had trouble sleeping because it didn't make sense. Today when I restarted the program it worked perfectly. I think one of my memoized functions what not being called. but some other function (not the memoized ones, but one which it calls) had been edited after the value was memoized. What's the lesson learned?

👍 3
Jim Newton 2020-10-08T10:57:39.029Z

I have a collection of data structures all of the same Record with several keyword fields. Some of the records are accessed seldomly. There is a particular field whose value is expensive to compute. I'd like to compute it only the first time it is needed, if at all. Thereafter, I'd like that value used for subsequent accesses to the field. Is there some mechanism for this? Or do I need to invent this myself?

gon 2020-10-08T11:27:01.029700Z

take a look at https://clojuredocs.org/clojure.core/delay

✔️ 2
borkdude 2020-10-08T11:49:57.030500Z

@jimka.issy as already answered, delay can be used, but memoize is also particularly handy for caching function results

Jim Newton 2020-10-08T11:52:43.030800Z

Can memoize be used for caching local function results?

borkdude 2020-10-08T12:04:02.031500Z

@jimka.issy Yes, (let [f (fn [x] x) f (memoize f)] ...)

xceno 2020-10-08T12:13:04.032300Z

Are there any examples out there that integrate a clojure backend with AWS Cognito? My searches didn't bring anything up

Matias Francisco Hernandez Arellano 2020-10-08T12:43:42.034400Z

Hi folks.. is it possible (i know it is but not sure how) have a plugin/drop-in architecture?: Mi idea is to be able to add a command to my bot by just adding the corresponding files into the corresponding namespace under discord-bot.commands where the command have to at least implent the handler function. Then "somehow" (this is the tricky part) import all of the discord-bot.commands.* into the core and call the handler function of each one.

➕ 1
Matias Francisco Hernandez Arellano 2020-10-08T12:43:50.034700Z

How can that import be implemented?

jsyrjala 2020-10-08T12:58:30.035200Z

Do you want the import happen at compile time or while the application is running?

2020-10-08T13:00:37.035300Z

I think I did something similar a while ago. It involved using https://clojuredocs.org/clojure.core/all-ns to get the relevant namespaces. From there, use https://clojuredocs.org/clojure.core/ns-resolve to get the var holding each handler fn. Then call it 🙂

👍 1
Jim Newton 2020-10-08T13:01:47.035500Z

does the cache prevent the local function from getting GC'ed ?

borkdude 2020-10-08T13:04:14.035700Z

not really, since the state of that function is also local. but note that arguments that are used for memoization will be stored in an atom, so as long the memoized function lives, the arguments don't get GC-ed

borkdude 2020-10-08T13:05:57.036Z

this actually bit me in clj-kondo since I had a global memoized function, so all the args that were passed to it, lived forever

Daniel Stephens 2020-10-08T13:42:44.038800Z

Does anyone know if there is either a way to make IntelliJ Cursive not format comments, or make cljfmt format comments? We have cljfmt as our formatter shared across the team, Cursive format seems slightly different but normally if it changes something it gets undone when I run cljfmt. Unfortunately Curisve also likes formatting comments and cljfmt doesn't touch these meaning any changes the Cursive formatter makes, sticks and causes a lot of noise in PRs.

walterl 2020-10-08T16:00:29.039100Z

Metabase has a fairly mature implemenation: https://github.com/metabase/metabase/blob/master/src/metabase/plugins.clj

💪 1
j 2020-10-08T16:18:57.041700Z

How do I run something repeatedly? for instance: (repeatedly-run 100 (say-this "hello") . I noticed that there is a repeatedly , but the function called can't take an argument?

João Galrito 2020-10-08T16:20:32.043Z

I have a thread running in the background that takes in 2 inputs from a queue and produces an output. Is there any way that I can store the latest inputs/outputs somewhere and print them in the repl when I want to check what it's doing?

João Galrito 2020-10-08T16:20:58.043400Z

I've tried an atom but it just blocks

ghadi 2020-10-08T16:22:01.043800Z

atoms never block

ghadi 2020-10-08T16:22:15.044200Z

can you post an example of your code @joao.galrito?

João Galrito 2020-10-08T16:22:16.044300Z

i suppose its the main thread that's blocking

João Galrito 2020-10-08T16:23:28.044600Z

yea it was the main thread that was blocking, because I was running the function directly from the REPL

João Galrito 2020-10-08T16:23:44.045Z

if i do (thread (my-fn)) now I can read the atom in the REPL

uosl 2020-10-08T16:25:27.045100Z

You can create a new function with your argument enclosed. Also it will return a lazy sequence, so if you're doing it for side-effects you should add a doall. This should work: (doall (repeatedly 100 #(println "hello")))

João Galrito 2020-10-08T16:25:31.045300Z

(defn repeatedly-with-args [f &amp; args] (repeatedly 100 #(apply f args))

j 2020-10-08T16:32:53.045700Z

thanks @regen for the doall tip!

j 2020-10-08T16:38:31.046300Z

@joao.galrito Thanks for that. I am trying to benchmark some code I wrote with time . I noticed that using time with repeatedl-with-args will output the time first, and then return all the results 100 times below it. This makes it hard to see the benchmark. is there a way to suppress the return output? or maybe put the timed benchmark below the return?

João Galrito 2020-10-08T16:49:24.046900Z

you can just return nil from the repeatedly-with-args

Chicão 2020-10-08T16:49:32.047200Z

hi, I need to subtract 3 hours from a date, may someone can help?

(t/minus (java.util.Date.) (t/hours 3))

João Galrito 2020-10-08T16:51:52.047600Z

maybe use Calendar?

João Galrito 2020-10-08T16:54:31.048900Z

(doto (Calendar/getInstance) (.add Calendar/WEEK_OF_YEAR -2))

Chicão 2020-10-08T16:58:24.049100Z

ty

schmee 2020-10-08T17:32:29.050900Z

don’t use either java.util.Date or java.util.Calendar unless you have to for legacy reasons, new code should use java.time

schmee 2020-10-08T17:32:46.051100Z

(-&gt; (Instant/now) (.minus 3 ChronoUnit/HOURS))

João Galrito 2020-10-08T17:53:01.051500Z

sucks that you can't add/subtract months with java.time though

schmee 2020-10-08T17:56:55.051700Z

you can, you need to use LocalDateTime or ZonedDateTime instead of Instant

👍 2
schmee 2020-10-08T17:57:11.051900Z

the docs here clarify how different classes are used: https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html

j 2020-10-08T18:04:20.052500Z

@joao.galrito how do i do that? do i wrap the body of repeatedly-with-args in a do call?

vlaaad 2020-10-08T18:04:44.052700Z

dotimes

vlaaad 2020-10-08T18:06:06.053Z

For benchmarking there is a library that does a bit more than runs code n times: https://github.com/hugoduncan/criterium

👍 1
j 2020-10-08T18:08:00.053300Z

@vlaaad I tried dotimesbut it doesn't seem to work if i ignore the argument binding (since I'm not going to use it)

j 2020-10-08T18:09:21.053500Z

@vlaaad also, I may have misunderstood your dotimes comment: was that a reference to my question to @joao.galrito above?

vlaaad 2020-10-08T18:10:50.053800Z

that was a reply to your initial question 🙂

j 2020-10-08T18:11:15.054Z

ahh, got it. thanks!

vlaaad 2020-10-08T18:11:30.054200Z

but for benchmarking you should use criterium, not measuring execution times...

vlaaad 2020-10-08T18:12:51.054500Z

what's not working with dotimes btw?

j 2020-10-08T18:12:54.054700Z

I see, I wanted some quick way to measure functions in clojure.core as part of my learning process. I thought time might be good because it was built in

vlaaad 2020-10-08T18:13:20.054900Z

time is good but too basic 🙂

vlaaad 2020-10-08T18:14:30.055100Z

for quick measuring should be fine, I guess

j 2020-10-08T18:14:39.055300Z

; dotimes requires exactly 2 forms in binding vector

j 2020-10-08T18:15:13.055500Z

i get that error message. I assume its because i don't put anything in the arguments vector

j 2020-10-08T18:15:31.055700Z

should i put some throwaway values to make dotimes work?

j 2020-10-08T18:17:20.055900Z

time gives me problems because I would like to run things 1000 times to get a proper average, but it has been quite difficult to wrap a function to repeat itself many times to feed to time

vlaaad 2020-10-08T18:22:25.056100Z

(dotimes [_ 100] (say-this "hello"))

vlaaad 2020-10-08T18:23:01.056300Z

yeah, you should have a name for local variable, it's common to use _ for unneeded stuff

j 2020-10-08T18:24:04.056500Z

That works! Thanks @vlaaad! I'll give criterium a try as well!

vlaaad 2020-10-08T18:24:49.056700Z

np 🙂

2020-10-08T19:21:13.057500Z

i thought the current best practice was to use java.time

2020-10-08T19:22:11.057700Z

what @schmee said 🙂

Matias Francisco Hernandez Arellano 2020-10-08T20:47:07.060200Z

Thanks Folks will check that

Matias Francisco Hernandez Arellano 2020-10-08T20:50:52.060700Z

Build time

Santiago 2020-10-08T21:13:24.061400Z

I got this serialized json array a:3:{s:1:"a";i:1;s:1:"b";i:2;s:1:"c";i:3;} how would I parse this in Clojure?

dogenpunk 2020-10-08T21:20:39.061800Z

@slack.jcpsantiago are you getting that from a PHP/Laravel API?

vlaaad 2020-10-08T21:25:58.062400Z

doesn't look like json.. is it bencode?

dogenpunk 2020-10-08T21:26:29.063Z

I’m pretty sure I came across that when writing an integration with an API implemented in PHP.

dogenpunk 2020-10-08T21:26:39.063300Z

that format, I mean

Santiago 2020-10-08T21:27:57.064200Z

it’s possible, I got it from a CSV file in which one column has these serialized arrays. I think it’s PHP but I’m not sure

dogenpunk 2020-10-08T21:29:14.064500Z

In which case, I used this library https://code.google.com/archive/p/serialized-php-parser/

sova-soars-the-sora 2020-10-08T22:23:34.064800Z

Got you with the ultimate clojure date hack.

sova-soars-the-sora 2020-10-08T22:23:49.065Z

(let [le-now (.format (java.text.SimpleDateFormat. "yyyyMMddHHmmss") (new java.util.Date)) n-hours-ago (.format (java.text.SimpleDateFormat. "yyyyMMddHHmmss") (new java.util.Date (- (System/currentTimeMillis) (* 3 60 60 1000))))] (println "now: " le-now) (println "3 hrs ago: " n-hours-ago))

👍 1
🎉 1