clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
caleb.macdonaldblack 2021-06-25T11:07:40.417Z

I’ve always used parinfer. What’s a good alternative I should learn?

Grzegorz Smajdor 2021-06-25T11:08:35.417100Z

https://github.com/DogLooksGood/parinfer-mode

Grzegorz Smajdor 2021-06-25T11:10:03.417400Z

if you emacs …. with vim I use https://www.vim.org/scripts/script.php?script_id=3998

2021-06-25T11:13:43.417600Z

lispy for emacs https://github.com/abo-abo/lispy

Helins 2021-06-25T12:12:15.418800Z

I saw this solution for clearing the terminal. It works on Linux (gnome terminal) but I am curious if it is portable:

(print "\033[H\033[2J")
  (flush)

ghadi 2021-06-25T12:16:02.419100Z

no mention of paredit? that's the gold standard

☝️ 2
borkdude 2021-06-25T12:16:25.419600Z

It works on macOS as well, I will try in windows cmd.exe...

borkdude 2021-06-25T12:17:36.419800Z

doesn't work in cmd.exe with the native console, but it does work if you use the new Terminal app on Windows

Apple 2021-06-25T12:18:06.420100Z

search "vt100 clear screen"

👍 1
borkdude 2021-06-25T12:19:04.420600Z

so it seems to be fairly portable, assuming everyone will use the new terminal on Windows

👍 1
Helins 2021-06-25T12:21:47.421600Z

Great, otherwise the alternative is to detect the OS, then run the appropriate shell command... This quasi oneliner is preferable

borkdude 2021-06-25T12:23:23.422100Z

@adam678 On windows I think you can detect the new terminal usage using an environment variable

borkdude 2021-06-25T12:26:33.422300Z

user=> (System/getenv "WT_SESSION")
"c1d08d7a-69ae-4dc2-93e6-8689e6789dd1"

💯 1
2021-06-25T12:37:52.422900Z

I take it if you're NOT in the new terminal, this will return an empty string instead of a UUID, correct?

borkdude 2021-06-25T12:45:07.423100Z

it will return nil probably

2021-06-25T12:54:19.424400Z

Yeah depends what editor you use. In my case it's Emacs/EVIL, so you go with Lispy/Lispyville. Lispyville integrates nicely with EVIL.

Endre Bakken Stovner 2021-06-25T14:23:06.425900Z

When developing I'd find it useful to have a get/`get-in` that errors in case the key isn't found. Is something like that built in?

Apple 2021-06-25T14:25:16.426600Z

get m k <pivot>) check pivot

Apple 2021-06-25T14:25:42.427Z

or find first but it does not work for get-in

Endre Bakken Stovner 2021-06-25T14:27:11.427400Z

I guess I could just (get m k (Exception.)).

Endre Bakken Stovner 2021-06-29T14:35:15.137900Z

@michael348 Yes, good idea!

Endre Bakken Stovner 2021-06-25T14:27:53.427500Z

Nope, did not work.

2021-06-25T14:30:11.427700Z

That will return a new exception

2021-06-25T14:30:25.427900Z

since it’s dev-time only you could do something like:

(alter-var-root #'clojure.core/get-in
                  (fn [v]
                    (fn [m ks]
                      (let [v (v m ks ::not-found)]
                        (if (= v ::not-found)
                          (throw (ex-info "" {}))
                          v)))))

Endre Bakken Stovner 2021-06-25T14:31:16.428100Z

Cool! I was hoping for something like that. I guess I could add it to my dev/env.clj :thumbsup:

dgb23 2021-06-25T14:58:13.430800Z

providing not-found in get can be quite elegant: https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L7203

souenzzo 2021-06-25T15:00:03.431Z

It should break libraries that use get-in

2021-06-25T15:01:10.431900Z

I noticed take always returns a collection, while take-last might return nil. For instance when the input is empty;

(take 1 [])
=> ()
(take-last 1 [])
=> nil
Is there a rational for this behaviour?

alexmiller 2021-06-25T15:10:57.432300Z

from a seq point of view, these are the same

Endre Bakken Stovner 2021-06-25T15:11:10.432400Z

Good point.

2021-06-25T15:20:44.435100Z

Sure thing, i ran into this because I changed some code and now the api returned nil instead of an empty collection. Unfortunately not all our clients use languages with good nilpunning 😅

alexmiller 2021-06-25T15:21:55.435900Z

in general, you should guard against this by using (seq ...) in the if condition (or seq'ing the result) if passing it on

1
isak 2021-06-25T15:31:16.436200Z

What I've seen some people do is to just define sget and sget-in, which do the above. The "s" meaing "safe".

👍 1
raspasov 2021-06-25T15:40:56.436500Z

Might not be the most elegant solution, but you can use a library like timbre, for example:

(let [ret (get-in {:a {:b 42}} [:a :z] :not-found)]
 (timbre/spy ret)
 ret)
2021-06-25T15:38:24.354Z MBP.local DEBUG [ss.experimental.scratch:2] - ret => :not-found
=> :not-found
Then in production, you can:
(timbre/set-level! :trace)
… to skip the timbre/spy message.

😎 1
Endre Bakken Stovner 2021-06-25T16:25:32.441100Z

I have this super-weird behavior in my multithreaded code: When my error message is the following:

error-message (count missing-files)
My error message is printed: 1 But when I do error-message (str missing-files) nothing is printed (most likely because an error is thrown). How do I debug? The line in question is https://github.com/endrebak/everclear/blob/working/src/clj/everclear/state/jobs.clj#L128. Note that the use of tap> is not the problem either. It also fails when I try (println missing-files). Any ideas what might be wrong or how I can try to debug?

Endre Bakken Stovner 2021-06-28T12:05:59.081400Z

Thanks, will try tomorrow. No time today :thumbsup:

Endre Bakken Stovner 2021-06-29T14:35:27.138100Z

Worked wonderfully, thanks 🙂

👍 1
Endre Bakken Stovner 2021-06-25T16:26:47.441300Z

I've tried reproducing what happens here:

(require '[babashka.process :as p :refer [process]])
(require '[<http://clojure.java.io|clojure.java.io> :as io])
(def bb-process (process ["bash" "-c" "echo hi!"] {:shutdown p/destroy}))
(def java-proc (:proc bb-process))
(defn post-process []
  (throw (Exception. "Hi")))

(.thenApply
           (.onExit java-proc)
           (reify java.util.function.Function
             (apply [this p]
                 (post-process))))

(defn read-process [proc] ;; babashka/process
  (try
    (with-open [rdr (io/reader (:out proc))]
      (binding [*in* rdr]
        (loop []
          (let [line (read-line)]
            (when (not (nil? line))
              (println line)
              (recur))))))
    (catch Exception e
      (println "ooops\n" (pr-str e)))))
(read-process bb-process)
But this actually throws an error.

Endre Bakken Stovner 2021-06-25T16:30:33.443300Z

(type missing-files) ;; clojure.lang.APersistentMap$ValSeqs and (count missing-files) ;; 1 work, but when I try to touch the contents of missing-files for example with (type (first missing-files)) my function goes mum again and nothing is printed.

p-himik 2021-06-25T16:38:37.443700Z

What do you mean by "use of `tap>` is not the problem either"? Have you tried using it with e.g. Reveal?

2021-06-25T16:45:30.444Z

to be clear, tap&gt; does not display any stack trace if it fails, it just silently fails

2021-06-25T16:45:53.444200Z

you need try/catch inside your tapping function if you need to know it failed

Endre Bakken Stovner 2021-06-25T17:03:12.444400Z

But if I remove the tap and just add a print it also fails.

Endre Bakken Stovner 2021-06-25T17:04:47.444600Z

I should try to simplify my code. I just wanted to quickly throw together something that worked but I guess I need to simplify it to find the error.

Endre Bakken Stovner 2021-06-25T17:07:07.444800Z

Thanks for the tip about reveal! I'll look into it

greglook 2021-06-25T17:16:34.445Z

it sounds like one of the values in your collection is throwing an exception in its toString method

2021-06-25T17:22:31.445200Z

ins)user=&gt; (defn try-string [o] (try (.toString o) (catch Exception e (pr-str e))))
#'user/try-string
(ins)user=&gt; (try-string (reify Object (toString [_] (/ 1 0))))
"#error {\n :cause \"Divide by zero\"\n :via\n [{:type java.lang.ArithmeticException\n   :message \"Divide by zero\"\n   :at [clojure.lang.Numbers divide \"Numbers.java\" 188]}]\n :trace\n [[clojure.lang.Numbers divide \"Numbers.java\" 188]\n ..."
one way to approach it

2021-06-25T17:23:09.445400Z

works like regular str on values that don't throw

(ins)user=&gt; (try-string 1)
"1"

jdkealy 2021-06-25T17:45:11.447500Z

how can i parse a string to timestamp that has AM/PM in the string? I tried clj-time, but it seems to ignore the aa param in the formatter, Read instant date doesn't like the syntax either (def custom-formatter (f/formatter "yyyy-MM-dd HH:mm aa")) (str (f/parse custom-formatter "2021-06-28 03:00 pm")) =&gt; "2021-06-28T03:00:00.000Z" (str (f/parse custom-formatter "2021-06-28 03:00 am")) =&gt; "2021-06-28T03:00:00.000Z"

2021-06-25T17:50:15.448200Z

it's not hard to do it with raw interop

(cmd)user=&gt; (java.text.SimpleDateFormat. "hh:mm a" java.util.Locale/US)
#object[java.text.SimpleDateFormat 0xc4c0b41 "java.text.SimpleDateFormat@3264901b"]
(cmd)user=&gt; (.parse *1 "11:30 AM")
#inst "1970-01-01T19:30:00.000-00:00"

2021-06-25T17:50:36.448700Z

I added the explicit locale because "AM" and "PM" are not universal to all locales

2021-06-25T17:51:34.449200Z

oh, your issue is that the resulting string doesn't have am/pm - you need to use the date format a second time to get that

borkdude 2021-06-25T17:51:50.449500Z

I would prefer to use java.time in 2021

👍 1
2021-06-25T17:52:07.449800Z

(cmd)user=&gt; (java.text.SimpleDateFormat. "hh:mm a" java.util.Locale/US)
#object[java.text.SimpleDateFormat 0xc4c0b41 "java.text.SimpleDateFormat@3264901b"]
(cmd)user=&gt; (.parse *1 "11:30 AM")
#inst "1970-01-01T19:30:00.000-00:00"
(ins)user=&gt; (str (.format *2 *1))
"11:30 AM"

2021-06-25T17:52:09.450Z

that's fair

2021-06-25T17:52:34.450500Z

anyway, it's doable with dateformat, you just need to use the format method to get the format back out in a string is all

2021-06-25T17:54:08.451500Z

or, more helpful probably, you need to use the formatter function of whatever library you are using, which I assume must be there

2021-06-25T18:10:10.452800Z

@jdkealy clj-time calls the function you want unparse, but I agree with @borkdude that you should use something based on java.time, clj-time is deprecated

seancorfield 2021-06-25T18:37:11.453600Z

(`clj-time` is pretty clearly deprecated and has a big, bold notice at the top of the readme recommending folks use something based on Java Time instead!)

borkdude 2021-06-25T19:00:09.454500Z

I'm glad I went with java.time in babashka

seancorfield 2021-06-25T19:20:40.454900Z

I'm glad we have stopped using clj-time altogether at work!

borkdude 2021-06-25T19:21:13.455500Z

we're still using it at work because I'm extremely lazy when it comes to rewriting something which works quite well for a long time already

👍 1
jdkealy 2021-06-25T19:21:58.455700Z

Thanks y'all 🙂

jdkealy 2021-06-25T19:23:57.455800Z

Straight from the source 🙂 https://github.com/clj-time/clj-time/commit/26e0f3ad066612336fce3050a3ea845072cf26aa

1
borkdude 2021-06-25T19:25:23.456200Z

@seancorfield oh hah, I didn't realize you used to maintain that lib ;)

seancorfield 2021-06-25T19:45:44.457600Z

Yup, for years 🙂 because we used it at work. But recently I put in a "tech debt" Jira ticket to get us off it, mostly to raw Java Time, but some clojure.java-time wrapper code still exists. And we still have some date-clj code in play as well (I have another Jira ticket to get rid of that).

2021-06-25T19:58:56.460300Z

We have clojure.java-time as well, but lately we’ve been using plain interop more and more. I don’t know, plain interop seems a bit more transparent to me

💯 1
borkdude 2021-06-25T20:37:42.460600Z

there's also cljc.java-time (bb compatible): https://github.com/babashka/babashka/blob/master/doc/projects.md#cljcjava-time

Michael Gardner 2021-06-25T22:13:56.461300Z

I really wish assert returned its argument, since (assert (get ...)) feels really natural. Of course you can write your own

👍 1
Shantanu Kumar 2021-06-25T23:42:22.462800Z

Not sure if I'm asking in the right channel; I want to know how do people upgrade the clj tool on Linux systems. Just download the new installer version and run?

p-himik 2021-06-25T23:44:32.462900Z

Yep.

1