Fork me on GitHub
#beginners
<
2020-10-08
>
Tāhā01:10:19

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ā02:10:46

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

seancorfield03:10:43

@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

seancorfield03:10:13

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

Tāhā03:10:41

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

Tāhā03:10:48

But when I try doing that I get the error

seancorfield03:10:17

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

seancorfield03:10:39

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

seancorfield03:10:04

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

Tāhā03:10:20

Yep did that too

seancorfield03:10:46

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ā03:10:48

(defn insert-cookie
  [headers name value options]
  (->>
   (cond-> ()
     (::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

seancorfield03:10:58

No, that's not what I posted.

seancorfield03:10:05

{::keys [limit]}

Tāhā03:10:30

They're equivalent (I think)

seancorfield03:10:53

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

Tāhā03:10:38

I think I found the problem, just a sec

seancorfield03:10:21

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

seancorfield03:10:18

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

Tāhā03:10:25

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ā03:10:54

Here's the fixed code:

(defn insert-cookie
  [headers name value options]
  (->>
   (cond-> ()
     (::limit options) (conj (let [{:keys [::limit]} options]
                               (if (integer? limit)
                                 (str "Max-Age=" limit)
                                 (cond->>
                                  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)))

seancorfield03:10:02

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-johnny07:10:26

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-johnny07:10:47

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)

henryw37408:10:24

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

👍 3
Jim Newton09:10:47

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?

👍 9
Jim Newton10:10:39

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?

borkdude11:10:57

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

Jim Newton11:10:43

Can memoize be used for caching local function results?

borkdude12:10:02

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

Jim Newton13:10:47

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

borkdude13:10:14

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

borkdude13:10:57

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

xceno12:10:04

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

Matias Francisco Hernandez Arellano12:10:42

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.

3
Zor13:10:37

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 🙂

👍 3
Matias Francisco Hernandez Arellano20:10:07

Thanks Folks will check that

Matias Francisco Hernandez Arellano12:10:50

How can that import be implemented?

jsyrjala12:10:30

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

Daniel Stephens13:10:44

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.

j16:10:57

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?

herald16:10:27

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 Galrito16:10:31

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

j16:10:53

thanks @UCW9TUDNK for the doall tip!

j16:10:31

@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 Galrito16:10:24

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

j18:10:20

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

vlaaad18:10:06

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

👍 3
j18:10:00

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

j18:10:21

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

vlaaad18:10:50

that was a reply to your initial question 🙂

j18:10:15

ahh, got it. thanks!

vlaaad18:10:30

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

vlaaad18:10:51

what's not working with dotimes btw?

j18:10:54

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

vlaaad18:10:20

time is good but too basic 🙂

vlaaad18:10:30

for quick measuring should be fine, I guess

j18:10:39

; dotimes requires exactly 2 forms in binding vector

j18:10:13

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

j18:10:31

should i put some throwaway values to make dotimes work?

j18:10:20

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

vlaaad18:10:25

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

vlaaad18:10:01

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

j18:10:04

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

João Galrito16:10:32

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 Galrito16:10:58

I've tried an atom but it just blocks

ghadi16:10:01

atoms never block

ghadi16:10:15

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

João Galrito16:10:16

i suppose its the main thread that's blocking

João Galrito16:10:28

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

João Galrito16:10:44

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

Chicão16:10:32

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

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

sova-soars-the-sora22:10:34

Got you with the ultimate clojure date hack.

sova-soars-the-sora22:10:49

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

👍 3
🎉 3
João Galrito16:10:52

maybe use Calendar?

João Galrito16:10:31

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

schmee17:10:29

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

schmee17:10:46

(-> (Instant/now) (.minus 3 ChronoUnit/HOURS))

João Galrito17:10:01

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

schmee17:10:55

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

👍 6
Michael Stokley19:10:13

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

Santiago21:10:24

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?

dogenpunk21:10:39

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

vlaaad21:10:58

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

dogenpunk21:10:29

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

dogenpunk21:10:39

that format, I mean

Santiago21:10:57

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