Fork me on GitHub
#beginners
<
2022-10-10
>
leif03:10:40

If I want to shadow let in a module, I get the following warning:

Clojure 1.11.1
user=> (ns test.core)
nil
test.core=> (def let 42)
WARNING: let already refers to: #'clojure.core/let in namespace: test.core, being replaced by: #'test.core/let
#'test.core/let
Of course, it then goes ahead and binds let.

pppaul03:10:30

can you exclude the let from clojure?

pppaul03:10:42

(:refer-clojure :exclude [ancestors printf])

leif03:10:12

Is there anyway to either disable that warning (for that one shadow), or is there a better way to shadow let?

Ngoc Khuat03:10:40

In the ns that you want to use test.core/let you should excludes the core let with

(ns your-ns
  (:refer-clojure :exclude [let]))

leif03:10:07

Ah, thanks.

leif03:10:48

(Basically, I just want to let other namespaces use test.core/let, which is different from clojure.core/let.

leif03:10:05

I've also tried (def test.core/let 42), but I still get the same warning.

Olaleye Blessing08:10:19

I'm trying to bootstrap a new clj project but I keep getting an error. File: ~/.clojure/deps.edn

{:aliases
  {:new {:extra-deps {seancorfield/clj-new
                       {:mvn/version "1.1.243"}}
         :exec-fn clj-new/create
         :exec-args {}}}} 
command: clj -X:new :template figwheel-main :name learn-cljs/weather :args '["+deps" "--reagent"]' error: No function found on command line or in :exec-fn I ran the command inside my powershell.

Ed10:10:33

I don't have a windows to test this with, but I suspect you may need to read this: https://clojure.org/reference/deps_and_cli#quoting

Ed10:10:26

your deps.edn file and command line work fine for me on my linux box, so it's likely to be a windows / powershell problem

Olaleye Blessing14:10:37

Thanks, it's been resolved. I misinterpreted a step in the book I'm reading.

Kamuela09:10:57

Question about directory structure: I notice a resources top-level directory, is that a catch-all for any other files needed or is there a specific use in mind?

Ferdinand Beyer09:10:32

Clojure itself does not have an opinion on that, it depends on the build conventions you use. Some projects prefer the "maven" layout, e.g. src/main/clojure for Clojure files and src/main/resources for resources. Resources are files that you need during runtime and can load via the JVM's class loader. In Clojure, there is for this. There are a lot of usage examples: • Static HTML files • logger configurations • Project metadata • ..

Ferdinand Beyer09:10:05

Having said that, many Clojure projects prefer a simpler layout, having src, test and resources top-level directories.

practicalli-johnny16:10:49

I use the resources directory for all the files I need to access once I package the Clojure project in an uberjar, e.g. configuration files such as config.edn used by integrant. I use resources/public to serve static website content such as HTML pages, CSS, .js files, etc

R Forgey13:10:26

Hello everyone. I'm trying to detect and count a number of substrings within a string, but re-seq appears to jump to the end of any substring it detects, missing other substrings consisting of characters belonging to already detected substrings. Is there a simple way to have it advance one character at a time, or do I have to rethink my approach?

(defn pattern-count [text pattern]
  (count (re-seq (re-pattern (str pattern)) text)))

(pattern-count "abababa" "aba")
This should be returning 3, but it only turns up 2

rolt13:10:25

interesting, someone asked the same question a few days ago. Out of curiosity, where does this problem comes from ?

rolt13:10:14

i don't think your approach will work, you could use partition instead

skylize15:10:44

It should not be super-difficult to loop over or reduce a string, calling re-find on each successive step. But there are definitely edges you would need to handle and/or make decisions about. e.g. • If the pattern starts with ^ you need to only try to match once. Otherwise you need to prepend ^ to the pattern, so that early steps don't duplicate matches from later steps. • If there are capture groups, you will need to either parse out non-groups, or flatten results to include them. Btw, you do not need to call str on a regex pattern to make it suitable input to re-pattern.

(re-pattern #"foo") ;; #"foo"
(re-pattern "foo")  ;; #"foo"

sb09:10:42

Im sure that isn’t the best solution.. but just for example.. I added reductions to see process output. I hope that could be helpful

(reductions (fn [{:keys [cnt processed]} input-char]
              (let [last-3-elem (conj (into [] (take-last 2 processed)) input-char)]
                (println "processed" last-3-elem)
                {:cnt (if (= last-3-elem (vec "aba"))
                        (inc cnt) cnt)
                 :processed (conj processed input-char)}))
            {:cnt 0 :processed []}
            (vec "abacababa"))

sb09:10:06

so main idea (reduce fn result input) and from this you can create a transducer.. too. I played before this with that when I tried to understand https://gist.github.com/damesek/00e8369e14beb7e4a14d9aef6ff74d0a

sb09:10:40

processed [a]
processed [a b]
processed [a b a]
processed [b a c]
processed [a c a]
processed [c a b]
processed [a b a]
processed [b a b]
processed [a b a]
=>
({:cnt 0, :processed []}
 {:cnt 0, :processed [\a]}
 {:cnt 0, :processed [\a \b]}
 {:cnt 1, :processed [\a \b \a]}
 {:cnt 1, :processed [\a \b \a \c]}
 {:cnt 1, :processed [\a \b \a \c \a]}
 {:cnt 1, :processed [\a \b \a \c \a \b]}
 {:cnt 2, :processed [\a \b \a \c \a \b \a]}
 {:cnt 2, :processed [\a \b \a \c \a \b \a \b]}
 {:cnt 3, :processed [\a \b \a \c \a \b \a \b \a]})

rolt11:10:08

for completion with partition something like this should work:

(->> text
     (partition (count pattern) 1)
     (map #(apply str %))
     (filter #(= % pattern))
     (count))

sb12:10:59

I totally forget here is a pad option at pratition fn (partition n step pad coll).

theequalizer7313:10:24

Hi friends, I'm having a problem running https://github.com/sunng87/diehard. On a clean project it works fine, but on my project is throwing this:

Unhandled java.lang.IncompatibleClassChangeError
   Method 'dev.failsafe.RetryPolicyBuilder dev.failsafe.RetryPolicy.builder()'
   must be InterfaceMethodref constant

                  core.clj:   63  diehard.core/retry-policy-from-config
                  core.clj:   60  diehard.core/retry-policy-from-config
I'm running this simple example from their docs
(dh/with-retry {:retry-on          Exception
                  :max-retries       3
                  :on-retry          (fn [val ex] (prn "retrying..."))
                  :on-failure        (fn [_ _] (prn "failed..."))
                  :on-failed-attempt (fn [_ _] (prn "failed attempt"))
                  :on-success        (fn [_] (prn "did it! success!"))}
    (throw (ex-info "not good" {:not "good"})))
Any hints on how to start debugging this?

rolt14:10:11

i'd check for duplicate versions of failsafe in the dependency tree

theequalizer7314:10:33

I tried lein deps :tree but apparently there are no duplicated versions

[diehard "0.11.6"]
   [dev.failsafe/failsafe "3.3.0"]
   [org.clojure/spec.alpha "0.3.218"]

rolt14:10:55

you already tried lein clean i suppose ?

theequalizer7314:10:25

Yes I did, also deleted my .m2 folder

rolt14:10:48

i'm really not sure what could cause that, some weird clojure aot + javac -target options ?

rolt14:10:31

maybe manually deleting the target folder in your project ?

theequalizer7314:10:45

I deleted the target folder as well, same exception

rolt14:10:58

i mean it really does sound like stale bytecode but i don't see anything else you can clean...

Chris16:10:24

Hi, I have an oauth token that I should reuse (otherwise I’ll be blocked for some time if I get more than X new tokens within a specified time), but I’m not sure how that works regarding state. I get the token and the amount of seconds it will expire in back, how can I reuse an existing token and get a new one if it’s already expired?

Ferdinand Beyer16:10:56

You can use an atom to store your token. Then check the expiry date to renew it

👍 1
Chris16:10:25

with a global scope?

pppaul18:10:44

yes, use (Def my-atom (atom nil))

👍 1
Ryan17:10:18

How would I wrap these assoc-in's to only run when the assoc-ing value isn't nil?

{:db (-> db (assoc-in [:app :auth :session :refresh-token] passed-refresh-token)
            (assoc-in [:app :ordering :order :order-id] passed-order-id))}

rolt17:10:29

a bit repetitive but:

(cond-> db
  (some? passed-refresh-token) (assoc-in [:app :auth :session :refresh-token] passed-refresh-token)
  (some? passed-order-id) (assoc-in [:app :ordering :order :order-id] passed-order-id))

rolt17:10:34

you could also write your 'maybe-assoc-in' function if you use it a lot

🙏 1
Ryan17:10:01

awesome thanks, the thread macro syntax was throwing me off a bit 🙂

pppaul18:10:20

cond-> is great, it will check all preds, so it's different from cond in that way. so you have to be sure that you are ok with that

skylize19:10:05

Do you want to return nil if any of the values are nil, or should db be passed through, with values maybe assoc'ed on? For the case of "fail on any nil", some->> works quite nicely.

(-> db
    (#(some->> passed-refresh-token
               (assoc-in % [:app :auth :refresh-token])))
    (#(some->> passed-order-id
               (assoc-in % [:app :ordering :order :order-id]))))
But just adding or to that, to forward an unchanged db, is enough to make the whole thing really ugly and confusing.
(#(or (some->> passed-refresh-token
               (assoc-in % [:app :auth :refresh-token])) %))
Either way, some->> makes for a very clean util function.
(defn assoc-in-when [m p x]
  (or (some->> x (assoc-in m p)) m))

kotlenik20:10:46

Hi guys! I have issues with clj repl. For each form I enter I get first 7 characters repeated.

kotlenik20:10:26

All works correct, but after each enter, first 7 characters are replicated at the beginning. System in question is MacOS Monterey 12.6 Intel, Java 19, Clojure CLI 1.11.1.1165 . lein repl works fine. The only thing that has changed in last 2 weeks since I was last time using clj (most of the time I use lein) is Java upgrade from 18 to 19 through brew.

pppaul20:10:40

could you give an example of what you expect to happen? I can't tell what is wrong from your current example

kotlenik21:10:26

Sure. If am not wrong, these 7 characters in yellow rectangular should not appear, right?

Alex Miller (Clojure team)21:10:04

how/when do they appear? like as you are typing the expression? or are you pasting?

kotlenik21:10:20

while I type them, all good. After enter, form gets evaluated, but also these 7 characters inserted all of sudden.

Alex Miller (Clojure team)21:10:36

also try with clojure (no rlwrap)

kotlenik21:10:44

clojure works fine @U064X3EF3, thanks! What may be the case with clj?

Alex Miller (Clojure team)21:10:50

clj just wraps clojure with rlwrap

Alex Miller (Clojure team)21:10:00

what's in your ~/.inputrc ?

Alex Miller (Clojure team)21:10:29

might be some config there that is affecting things

kotlenik21:10:39

understood. I do not have ~/.inputrc present

Alex Miller (Clojure team)21:10:30

what about echo $INPUTRC ?

Alex Miller (Clojure team)21:10:53

maybe also useful to check rlwrap -v (although can't say I know of any particular version that's problematic). I'm using 0.45

kotlenik21:10:01

INPUTRC env var is not defined. rlwrap is also 0.45 here.

Alex Miller (Clojure team)21:10:13

I suspect it's probably not a coincidence that "user=> " is 7 characters long too

Alex Miller (Clojure team)21:10:19

if you (ns a) , do you then see 4 characters repeated?

Alex Miller (Clojure team)21:10:55

when rlwrap thinks it's in a prompt it has the ability to rewrite the prompt by deleting the program's prompt and rewriting it - seems like some disconnect there with your terminal settings

kotlenik21:10:26

understood! Thanks @U064X3EF3! will dig in to see what may be the glitch. Thanks also to @U0LAJQLQ1!

seancorfield21:10:11

I've had strange behavior with rlwrap 0.45 lately -- weird behavior with locales, duplicating the user=> prompt, and so on. I haven't figured out a reliable fix for all of the behaviors yet.

Alex Miller (Clojure team)21:10:41

there's some hints in that last link maybe

seancorfield21:10:22

Ah, locale can cause that sort of thing it seems? I had to set LANG=C in /etc/default/locale (I think some Ubuntu upgrade had changed that to LANG="C.UTF-8" and rlwrap didn't like it. That seemed to fix most of the problems I was having over the weekend.

seancorfield21:10:18

At some point in the past, I also had to add this:

$ cat ~/.inputrc
$if clojure
    set enable-bracketed-paste off
$endif
to workaround this problem: https://github.com/hanslub42/rlwrap/issues/131

kotlenik21:10:44

Ok, fixed with defining ~/.inputrc as you guys advised @U04V70XH6 @U064X3EF3, thanks! 🙌 LANG unchanged in .zshrc .

kotlenik21:10:44

To be honest, still not sure why all of these, but night is young...

Alex Miller (Clojure team)21:10:45

we are but enchanters, repeating the magical spells from our books

😄 2
💪 1
skylize21:10:23

Incorrectly escaped values in $COMMAND AND $PROMPT can also definitely lead to this type bug.

kotlenik21:10:23

Hm, I have that custom PS1 configured, but it has not been changed for months. Also clj, in this install, was working quite a long time. Will test, thanks for the hint @U90R0EPHA!

skylize22:10:01

Yeah. I actually meant $PS1 and $PROMPT_COMMAND. 😺 Sorry it doesn't help you, but definitely worth remembering in the future.

kotlenik22:10:10

Np @U90R0EPHA, just brought back default ones for .zshrc and disabled .inputrc , and it all went back to old errors. When I have returned .inputrc , it again works fine. Thanks for the heads up!

seancorfield22:10:50

My $PS1:

# 
PS1="\n\d \t\n\[\e[32;1m\](\[\e[37;1m\]\w\[\e[32;1m\])-(!\!\[\e[32;1m\])-> \[\e[0m\]"

seancorfield22:10:29

Shows blank line, date/time, (directory)-(!<history-id)->

skylize22:10:34

Looks to me like all your escape codes are properly closed there. (So hard to read anything with escape codes. 😒)

seancorfield23:10:46

It is extremely hard to read, I agree. It used to be quite a bit more complex, as copied from that website, but I removed several things. And, yes, all the escape sequences are closed. I just wanted to paste a complex example of $PS1 that doesn't interfere with rlwrap 🙂

zane19:10:38

This just bit me as well. Disabling bracketed paste in .inputrc resolved the issue. Thanks, @U04V70XH6!

❯ rlwrap -v
rlwrap 0.45

Eric22:10:22

Clojure newbie here. I’m looking for a way to convert a map of strings (read from csv) into their proper data types given a map of conversion functions. Any ideas? What I have:

(def conversions {:name identity, :age str->int, :role keyword)

(def data '({:name "Bob", :age "45", :role "employee"} etc.))
What I want:
'({:name "Bob", :age 45, :role :employee} etc.)

Daniel Craig22:10:42

(def conversions {:name identity, :age #(Integer. %) :role keyword})

(def data [{:name "Bob", :age "45", :role "employee"}])

(let [name-fn (:name conversions)
      age-fn (:age conversions)
      role-fn (:role conversions)]
  (map (fn [{:keys [name age role]}] {:name (name-fn name) :age (age-fn age) :role (role-fn role)}) data)
)

Ted Ciafardini22:10:59

I think you want something like this ?

(->> data
     (map (comp identity :name))
     (map (comp str->int :age))
     (map (comp keyword :role)))
and if you are pressed on using the conversions map:
(->> data
     (map (comp (get conversions :name) :name))
     (map (comp (get conversions :age) :age))
     (map (comp (get conversions :role) :role)))

Daniel Craig22:10:21

oh yeah nice use of comp

Ted Ciafardini22:10:43

^^ I like your method of bulding a map rather than calling map 3 times

seancorfield22:10:03

@U02QSGN57N1 You might want to try that in the REPL -- I don't think it does what you think it does...

seancorfield22:10:05

(->> data
     (map (comp (get conversions :name) :name))
     (map (comp (get conversions :age) :age))
     (map (comp (get conversions :role) :role)))
Error printing return value (NumberFormatException) at java.lang.Integer/parseInt (Integer.java:627).
Cannot parse null string

seancorfield22:10:31

The first map produces a list of the names -- it's no longer a list of hash maps.

Edward Ciafardini22:10:25

^oops 😳 Foolishly didn’t REPL it & away from computer. Sorryyyy

seancorfield22:10:23

Here's another solution:

user=> (map #(reduce (fn [m k] (update m k (conversions k))) % (keys conversions)) data)
({:name "Bob", :age 45, :role :employee})
user=>
No need to manually itemize the keys in conversions.

👍 3
seancorfield22:10:54

Or maybe this if you prefer for over map/`reduce`:

user=> (for [item data]
         (into {} (for [[k f] conversions]
                    [k (f (item k))])))
({:name "Bob", :age 45, :role :employee})
user=>

Eric23:10:05

Nice, thanks for the help everyone.

Eric23:10:15

I found update-in, but I couldn’t get it to work:

(map (fn [m k] (update-in m [k] (k conversions))
       data
       (keys conversions)))

skylize00:10:25

update-in is for traversing down to a point in a nested structure to update a single value.

(let [foo {:bar {:baz [{:change-me 5}]}}]
  (update-in foo 
             [:bar :baz 0 :change-me]
             inc))

;;  {:bar {:baz [{:change-me 6}]}}

Eric00:10:38

Oh, that makes sense. Thanks for pointing out the difference.