beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
solf 2021-03-08T02:03:24.008900Z

You mean removing

(loop [stack stack]
? I don't think it's needed, actually. recur target both loop and functions, so the loop is redundant here.

sb 2021-03-08T09:58:42.010800Z

If I have two different kind of output like vector or map -> like in this topic https://gist.github.com/damesek/d6a371ce56983e77872cd7a574f548bb is that possible use comp in some way? I can solve with two different function or not so nice solution like this.. but is that possible nicer?

(reduce (-transform-to-map)
          {}
          (reduce (-vec-frequencies)
                  []
                  data))

raspasov 2021-03-08T10:13:20.010900Z

(.indexOf …) is O(n) so watch out if you’re applying this to large collections, it can be quite slow.

sb 2021-03-08T10:14:05.011100Z

True, I totally forget.. thanks!

👍 1
Hagenek 2021-03-08T10:19:28.012Z

Is there any way for me to use java methods as predicate functions in Clojure? I just discovered that while the first function works, the second doesnt:

(defn letters? [s]
  (not= (count (filter #(if (Character/isLetter %)
                          true
                          false) s))
        0))
(defn letters2? [s]
(some Character/isLetter s))

borkdude 2021-03-08T10:37:30.012400Z

@georghagen Just wrap in a function: (some #(Character/isLetter %) s))

Hagenek 2021-03-08T10:39:01.012900Z

Thanks 😃 I end up with this because nil is truthy, and more idiomatic way to write it? (defn letters? [s]   (if (some #(Character/isLetter %) s)     true     false))

2021-03-08T10:56:24.013800Z

Do you even need the “if” statement?

2021-03-08T11:10:04.016400Z

Oh, I think I see. You don’t want the truth/falsy values returned, you want it coerced.

Hagenek 2021-03-08T11:10:33.016600Z

That's right because it needs to be like that for the predicate function to work in some right?

borkdude 2021-03-08T11:11:26.016800Z

(def letters? (partial some #(Character/isLetter %)))

1
borkdude 2021-03-08T11:12:11.017Z

if you want to be fancy ;) I usually still write the defn out in full though (better static analysis for clj-kondo for example)

Hagenek 2021-03-08T11:12:43.017300Z

Ahh ok, yeah it gets to be a point where you can just write it out, Clojure is very terse to begin with also so 😃

2021-03-08T11:31:11.017700Z

if you are doing (if test true false), can you replace this with just (boolean test) Or am I misunderstanding?

borkdude 2021-03-08T11:31:49.018200Z

correct

👍 1
ajithkumar 2021-03-08T11:40:33.019600Z

Hey everyone, Is there anyway to convert "false" string to Boolean in Clojurescript?

raspasov 2021-03-08T12:01:56.020400Z

(let [s "false"
      t-or-f (cljs.tools.reader.edn/read-string s)]
  (println [t-or-f (type t-or-f)]))
=> [false #object[Boolean]]

👍 1
Lisbeth Ammitzbøll Hansen 2021-03-08T12:13:33.025400Z

Hi. How do I traverse this kind of data structure and return a value of total minutes for each key :

(defn time-regs
  []

  {"<mailto:some@example.org|some@example.org>"  [{:user_id 1, :email "<mailto:some@example.org|some@example.org>", :time-registered "0 hrs, 0 min"} 
                        {:user_id 1, :email "<mailto:some@example.org|some@example.org>", :time-registered "2 hrs, 20                        
                          min"}], 
   "<mailto:other@example.org|other@example.org>"  [{:user_id 3, :email "<mailto:other@example.org|other@example.org>", :time-registered "0 hrs, 20 
                          min"}
                         {:user_id 3, :email "<mailto:other@example.org|other@example.org>", :time-registered "1 hrs, 20  
                          min"}]})
I want to traverse :time-regs and for every key ("<mailto:some@example.org|some@example.org>", "<mailto:other@example.org|other@example.org>") evaluate :time-registered with :
match (re-matches #"([0-9]+) hrs, ([0-9]+) min" time-reg)
and if there is a match, add minutes. Finally, I want to return something like :
{"<mailto:some@example.org|some@example.org>"   [:min-total 140]
  "<mailto:other@example.org|other@example.org>" [:min-total 100"]}
I have looked at doseq - it makes it possible to access my keys and values, but doesn't return a value. I have tried with map : (map #(% 0) (time-regs)), but can't find a way to access the values for the keys

raspasov 2021-03-08T12:32:32.026400Z

This is how to access the values of the keys:

(-&gt;&gt; (time-regs)
     (map
       (fn [[k vs]]
         [k [:min-total (mapv :time-registered vs)]]))
     (into {}))
=> {“<mailto:some@example.org|some@example.org>” [:min-total [“0 hrs, 0 min” “2 hrs, 20 min”]], “<mailto:other@example.org|other@example.org>” [:min-total [“0 hrs, 20 min” “1 hrs, 20 min”]]}

raspasov 2021-03-08T12:33:55.026600Z

I am not sure how to reliably convert things like “2 hrs, 20 min” to minutes; perhaps there’s a better format to return this in? If not, I’d look into date/time libraries that might support this kind of thing; ideally I’d like to avoid it and rely on something like a timestamp/date.

Jim Strieter 2021-03-08T13:24:03.030900Z

This question is about style. I wrote a function like this: (defn my-func [some-arg] (let [ x1 (whatever ...) x2 (whatever ...) y1 (whatever ...) y2 (whatever ...)] (fn [x] ( whatever)))) The point is to solve a boundary value problem, then return a function that is the solution to that problem. Here's my question: Am I guilty of writing C in Clojure?

Lisbeth Ammitzbøll Hansen 2021-03-08T13:26:37.031100Z

Thanks a lot for your answer 🙂. True about the time format (it's others code that I'm trying to test)

👍 1
simongray 2021-03-08T13:32:29.031300Z

You can use this function to convert the string-based :time-registered format to minutes

(defn time-registered-&gt;min-total
  [time-registered]
  (when-let [[_ hrs min] (re-matches #"(.+) hrs, (.+) min" time-registered)]
    (+ (* (Integer/parseInt hrs) 60)
       (Integer/parseInt min))))

(time-registered-&gt;min-total "2 hrs, 39 min")
;;=&gt; 159

👌 2
pavlosmelissinos 2021-03-08T13:46:59.032500Z

Sorry, it's not clear to me what you're doing. Where are you using those bindings? What about some-arg? What do you think is C-like about your example? Apart from your question, I think it's more common to start the bindings on the let line:

(defn my-func [some-arg]
  (let [x1 (whatever ...)
        x2 (whatever ...)
        y1 (whatever ...)
        y2 (whatever ...)]
    (fn [x] (whatever))))

teodorlu 2021-03-08T13:49:27.033100Z

I've seen @borkdude use _ as let symbols to just "collect side effcts". Something like

(defn my-func [some-arg]
  (let [x1 (whatever ...)
        _ (whatever ...)
        y1 (whatever ...)
        _ (whatever ...)]
    (fn [x] (whatever))))
if x2 and y2 are never used. You could see if you could refactor your functions for use in a pipeline (`->>` or -&gt;)

teodorlu 2021-03-08T13:50:11.033300Z

> Here's my question: Am I guilty of writing C in Clojure? Sure, that looks a lot like C. But Clojure is flexible for a reason. Writing imperative code in Clojure is fine, if you need imperative code.

✅ 1
Jim Strieter 2021-03-08T13:56:29.033500Z

What the function does is take 2 tuples (x1, y1), (x2, y2), and solve y=mx+b. Then return a function representing the y=mx+b.

Jim Strieter 2021-03-08T13:56:44.033700Z

Yeah I can tidy up the indents a bit

Jim Strieter 2021-03-08T13:58:53.034Z

@teodorlu yeah Haskell you hear that???

❤️ 1
2021-03-08T14:02:15.034300Z

I like to use a style like that to write “self-documenting” code--breaking the intermediate values out onto separate lines in a let allows you to assign them meaningful and informative names that will help future devs in understanding what exactly the function is doing.

✅ 5
2021-03-08T14:03:35.034500Z

Sometimes I’ll write an expression that uses the -&gt; threading macro, and then later I’ll come back and I have a hard time following the flow, so I’ll convert it back to a let statement with meaningful names for the intermediate values.

👍 1
Jim Strieter 2021-03-08T14:24:08.035100Z

@manutter51 yeah, that makes sense

Endre Bakken Stovner 2021-03-08T15:55:11.037600Z

I want to implement the following function in Clojure:

(facts "about `to-namedlist`"
       (to-namedlist {:plot "quals.svg" :data "quals.tsv"}) =&gt; {:plot "quals.svg" :data "quals.tsv" 0 "quals.svg" 1 "quals.tsv"}

       (to-namedlist ["bwa-map.bam" "bwa-map2.bam"]) =&gt; {0 "bwa-map.bam" 1 "bwa-map2.bam"}

       (to-namedlist "hg19.fasta") =&gt; {0 "hg19.fasta"})
It seems like I need to switch on the type of the input arg to do it. What is the most idiomatic way to achieve this?

Endre Bakken Stovner 2021-03-09T19:07:23.156700Z

I agree 🙂

2021-03-10T21:56:23.264200Z

also, you can use defmulti and use type as your dispatch function, that way you can add support for more types in a more elegant way, and defmulti automatically does proper subclass dispatch

(cmd)user=&gt; (defmulti foo type)
nil
(cmd)user=&gt; (defmethod foo java.util.Map [_] :map)
#object[clojure.lang.MultiFn 0x2e52fb3e "clojure.lang.MultiFn@2e52fb3e"]
(cmd)user=&gt; (foo {})
:map
(ins)user=&gt; (foo (java.util.HashMap.))
:map

raspasov 2021-03-08T15:58:30.037700Z

https://clojuredocs.org/clojure.core/cond is ok IMO

Endre Bakken Stovner 2021-03-08T16:08:06.038700Z

Thanks. I guess (case (type arg) ... would also be acceptable here then?

Endre Bakken Stovner 2021-03-08T16:08:27.039100Z

I know the below is abominable code... But even worse, it does not work as expected:

(defn switch-on-type [arg]
  (case (str (type arg))
    "java.lang.String" "String!!!"
    "clojure.lang.PersitentArrayMap" "Map!"
    "Dunno"))

(switch-on-type "hi") ; "Dunno" ;; I wanted "String!!!"

2021-03-08T16:11:09.040Z

Try making the last line (str "Dunno what " (type arg) " is."), it’ll help you pin down the problem.

Endre Bakken Stovner 2021-03-08T16:16:26.040600Z

Thanks for the suggestion. I am still unable to see my error. Btw, you see that I use (str (type arg)) in my case, right?

Aviv Kotek 2021-03-08T16:16:49.040800Z

using next.jdbc, how would it be possible to do batch-upsert (with column insertion), something like this: (this would work using java.jdbc)

(jdbc/execute! db ["INSERT INTO T(col1, col2) 
                    VALUES (?, ?)
                    ON DUPLICATE UPDATE
                    col2=values(col2)"
                   [["c1" "c2"]
                    ["c3" "c4"]]....more here] {:multi? true})

2021-03-08T16:41:47.041500Z

Yes, but including it in the last line should show you what you're getting that you're not expecting. What's it returning if you add that?

Endre Bakken Stovner 2021-03-08T16:49:04.041700Z

I still do not see my error... 😖 :duncecap:

Darin Douglass 2021-03-08T16:50:32.041900Z

why do you need to check the type explicitly? something like this is much clearer and likely meets your needs?:

(cond
  (string? arg) "String!!"
  (map? arg) "Map!"
  :else "dunno")

Endre Bakken Stovner 2021-03-08T16:56:16.042300Z

For some reason I was looking for a hash-map? but did not find it. It's been too long since I've used Clojure 😕

Darin Douglass 2021-03-08T16:56:45.042700Z

hash-map is a fn, so i can see the confusion 🙂 glad to help

Endre Bakken Stovner 2021-03-08T16:57:02.043Z

Yes, thank you 🙂

acim1 2021-03-08T17:08:18.043800Z

How does one typically document that a function throws an exception and which (if applicable)?

walterl 2021-03-08T17:17:00.044300Z

Most common in the standard lib is simply "Throws <exception> when <condition>" in the docstring.

walterl 2021-03-08T17:17:10.044600Z

Trying to think of an example, but spacing

Jim Strieter 2021-03-08T17:17:30.045100Z

Is currying considered idiomatic Clojure? If so, is there a rule of thumb for how to order the inputs in a function?

acim1 2021-03-08T17:18:04.045200Z

Gotcha. Pretty barebones but to the point, makes sense.

walterl 2021-03-08T17:25:11.045600Z

I see next.jdbc has execute-batch! (https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.1.643/api/next.jdbc#execute-batch!) and insert-multi! (https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.1.643/api/next.jdbc.sql#insert-multi!). Maybe one of those can help you. Otherwise I'd ask in #sql 🙂

alexmiller 2021-03-08T17:26:30.046Z

No

✅ 1
Aviv Kotek 2021-03-08T17:27:17.046200Z

yep, wanted to escape the "prepared-statement", but I guess i'd go with that now -- thanks!

Jim Strieter 2021-03-08T17:39:14.046400Z

Ok cool

2021-03-08T17:45:02.049100Z

There is partial but I rarely see it used, compared to eg ML languages. Since it’s so easy to create a lambda and that does away with the issue of input ordering I personally haven’t really felt that I’m missing it as an idiomatic approach.

seancorfield 2021-03-08T17:45:59.049300Z

I seem to recall Rich has said that he considers short, anonymous functions to be more idiomatic than partial (a long time ago, on the Clojure Google Groups mailing list).

➕ 1
2021-03-08T17:49:55.052100Z

This pragmatic approach is great and I’m happy to see it championed among clojurists. A lot of functional heavy languages also tend to cultivate a culture where terse compositions / pipelines are preferred and while I can see the appeal of elegance I think it can easily create hard to maintain code.

alexmiller 2021-03-08T18:02:20.052900Z

re arg order, see https://clojure.org/guides/faq#arg_order

robertfw 2021-03-08T18:52:04.053200Z

Adding another datapoint, I started off with terse code and after my first time working with others on a Clojure project, shifted to writing more verbose let binding blocks to improve readability. The trick for using _ for side-effecting steps within a let is also something I'll lean on, I think the code is much cleaner that way than using multiple lets, though if there are lots of them I consider it a code smell that has me looking to see if there is a better structure

2021-03-08T19:21:57.053400Z

There can be multiple different concrete types implementing each of these kinds of collections, e.g. PersistentHashMap and PersistentArrayMap for maps, plus a handful of others that are in less common use.

2021-03-08T19:22:32.053600Z

If all you care about is that it implements the Clojure map interface, then (map? x) is better than listing multiple concrete types, probably, for long term code maintenance.

👍 2
☝️ 1
Jan Ignatius 2021-03-08T19:43:57.054700Z

When using (print), is it possible to suppress the "nil" that follows the printout?

2021-03-08T19:46:08.055300Z

the nil that follows is the repl printing out the result of the call to print

Jan Ignatius 2021-03-08T19:46:24.055600Z

The compiled app would not print it?

2021-03-08T19:47:00.056600Z

code run outside of a repl won't

Jan Ignatius 2021-03-08T19:47:11.057Z

Ok. Then my fizzbuzz works 🙂

Jan Ignatius 2021-03-08T19:47:22.057300Z

I was trying to figure out how to remove that nil for a while now 😄

Jan Ignatius 2021-03-08T20:33:48.058700Z

Hmmh, it works in REPL but I can't seem to get the complied version to work. What is the correct syntax to pass on the CLI attributes to the -main function? This is what I have currently:

(ns test-world2.core
  (:gen-class))

(defn -main [&amp; args]

  (defn fizz-buzz-full [n]
    (cond
      (and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
      (zero? (mod n 3)) (println "fizz")
      (zero? (mod n 5)) (println "buzz")
      :else (println n)))
  
  (for [x (range 1 args)]
    (fizz-buzz-full x)
    )
  )

(-main)

Jan Ignatius 2021-03-08T20:35:58.059300Z

lein run 101 just fails with " Exception in thread "main" Syntax error compiling at (test_world2/core.clj:19:1)." 19 being the line for (-main).

dpsutton 2021-03-08T20:40:07.062100Z

couple things. don't nest defn. maybe you have experience with scheme? Just put (defn fizz-buzz [n] ...) outside of your main function. arguments passed from the command line are going to be strings. so you'll have "101" not 101 to contend with. If you're invoking (-main) with no args in your namespace it will call that with no args which will hit the code (range 1 nil) which will probably blow up. Can you post the error you've gotten here in a snippet?

Jan Ignatius 2021-03-08T20:45:07.063Z

Ok, I've broken the other function out from -main and added "(int args)" to avoid the string-issue:

(ns test-world2.core
  (:gen-class))

(defn fizz-buzz-full [n]
  (cond
    (and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
    (zero? (mod n 3)) (println "fizz")
    (zero? (mod n 5)) (println "buzz")
    :else (println n)
    )
  )
(defn -main [&amp; args]
   
  (for [x (range 1 (int args))]
    (fizz-buzz-full x)
    )
  )
;(-main)

Jan Ignatius 2021-03-08T20:46:31.063600Z

This resulted in a new error (yay!) on "Unable to resolve symbol: fizz-buzz-full in this context"

Jan Ignatius 2021-03-08T20:47:11.063900Z

The full error message is very long

dpsutton 2021-03-08T20:47:59.065Z

Yes. The function needs to be defined before it’s call. You can use triple backticks to have code blocks

Jan Ignatius 2021-03-08T20:52:13.066600Z

If I comment out the (-main) - as my understanding is that lein run will run (-main) automatically? I get an error on " class clojure.lang.ArraySeq cannot be cast to class java.lang.Character (clojure.lang.ArraySeq is in unnamed module of loader 'app'; java.lang.Character is in module java.base of loader 'bootstrap')"

Jan Ignatius 2021-03-08T20:53:03.067Z

I guess I'm handling the cli arguments in the wrong way.

Jan Ignatius 2021-03-08T20:54:46.067600Z

I updated the code block above to match the current version

dpsutton 2021-03-08T20:57:07.068700Z

Yeas the &args there should be some kind of sequence of strings. Most likely (“101”).

dpsutton 2021-03-08T20:57:23.069300Z

Do you have a repl running?

Jan Ignatius 2021-03-08T20:57:58.069500Z

Yeah, in VS code

Jan Ignatius 2021-03-08T20:58:38.070500Z

I get the same error if I uncomment (-main) and change it to (-main 101) and try to evaluate it.

dpsutton 2021-03-08T20:58:42.070700Z

Do you know how to send forms to the repl from your text file?

Jan Ignatius 2021-03-08T20:59:30.071600Z

Yes, the code worked flawlessly until I added the option of defining the range

dpsutton 2021-03-08T20:59:50.072300Z

Ok. So again & args will take all of the arguments in as a sequence of args. And you can’t call int on a collection

dpsutton 2021-03-08T21:00:59.073400Z

And it might be better to call main with “101” because that’s what it will look like when you call it from the command line

Jan Ignatius 2021-03-08T21:01:51.073700Z

So this works in REPL:

(ns test-world2.core
  (:gen-class))

(defn fizz-buzz-full [n]
  (cond
    (and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
    (zero? (mod n 3)) (println "fizz")
    (zero? (mod n 5)) (println "buzz")
    :else (println n)))


(defn -main [args]
   
  (for [x (range 1 (int args))]
    (fizz-buzz-full x)
    )
  )

(-main 101)

Jan Ignatius 2021-03-08T21:02:07.074Z

I dropped the & from the [args]

Jan Ignatius 2021-03-08T21:02:53.074400Z

But if I comment out (-main 101) and run lein run 101 I still get the error.

Jan Ignatius 2021-03-08T21:04:22.075900Z

Do I need to touch some of the other project files to somehow enable the arguments getting passed?

Jan Ignatius 2021-03-08T21:04:38.076500Z

I'm very, very new to clojure and lein

pavlosmelissinos 2021-03-08T21:04:50.076900Z

your problem is with the way you parse args in your main function

dpsutton 2021-03-08T21:04:54.077200Z

but that's not a valid signature for a main function

☝️ 1
Jan Ignatius 2021-03-08T21:06:00.078700Z

Ok, what does signature mean in this context? Is the -main function special?

dpsutton 2021-03-08T21:06:03.078800Z

now a common thing to do is make a main that has the normal &amp; args function, take the first arg, turn it into a numeric type rather than a string, and then call your function. and this way your function can just deal with a single numeric and do what it wants and ignore strings, and collections, and other ugliness

dpsutton 2021-03-08T21:07:15.080400Z

yes. its an entrypoint and takes a collection of strings. that's how it can be invoked from lein run. it kinda has to have that shape. but let's ignore that for now. write a function that takes a single long and does your fizz buzz stuff on it. practice it at the repl (fizzbuzz 7) does what you want it to do and then we can hook up the main function to that

Jan Ignatius 2021-03-08T21:08:43.081Z

Will do. Though tomorrow - it's past 2300 here. Thank you for your help and patience.

Jim Strieter 2021-03-08T22:30:21.082600Z

Thank you gents!

Jim Strieter 2021-03-08T22:30:25.082800Z

🙂