beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
pez 2021-03-09T00:04:16.084800Z

Sometimes partial just makes more sense to me than a lambda.

fabrao 2021-03-09T03:15:21.087600Z

Hello all, how do I dynamic generate param for this ?

(fnc "A" [["A" "B"][1 2]] "B" [["A" "B"][1 2]] ...)
I can generate that [[...]] but how to how to sequence "A" [[...]] "B" [[...]] ?

seancorfield 2021-03-09T03:16:42.088300Z

@fabrao Can you be a bit more specific over exactly what the relationship is between various parts of those arguments?

seancorfield 2021-03-09T03:16:57.088700Z

Are you asking about Spec/generators, or something else?

fabrao 2021-03-09T03:23:06.092100Z

Hello Sean, I have to make params for dynamic create workbook for excel lib mjul/docjure that use

(let [wb (create-workbook "Squares"
                          [["N" "N^2"]
                           [1 1]
                           [2 4]
                           [3 9]]
                          "Cubes"
                          [["N" "N^3"]
                           [1 1]
                           [2 8]
                           [3 27]])]
   (save-workbook! "exponents.xlsx" wb))
the create-workbook is a sequence of string array/array . But I have to make many string and arrays. How to I make it dynamic generating it?

seancorfield 2021-03-09T03:26:19.093200Z

You have a formula for calculating values here?

fabrao 2021-03-09T03:26:47.093700Z

no, just generate sheet and values for it

seancorfield 2021-03-09T03:26:58.094Z

Then I have no idea what you're asking.

seancorfield 2021-03-09T03:27:15.094400Z

If you have a formula for calculating the data then you can just generate it.

seancorfield 2021-03-09T03:27:33.094900Z

If you don't have a formula or a Spec, I'm not sure how you expect to automate this?

fabrao 2021-03-09T03:29:08.096400Z

Well , how can I generate `(fn "sheet1" [[...]] "sheet2" [[...]] ...)

seancorfield 2021-03-09T03:29:09.096500Z

The data you've shown can be generated -- there's a clear formula involved

fabrao 2021-03-09T03:31:36.097100Z

(defn create-workbook
  "Create a new XLSX workbook.  Sheet-name is a string name for the sheet. Data
  is a vector of vectors, representing the rows and the cells of the rows.
  Alternate sheet names and data to create multiple sheets.
  (create-workbook \"SheetName1\" [[\"A1\" \"A2\"][\"B1\" \"B2\"]]
                   \"SheetName2\" [[\"A1\" \"A2\"][\"B1\" \"B2\"]] "
 ([sheet-name data]
   (let [workbook (XSSFWorkbook.)
         sheet    (add-sheet! workbook sheet-name)]
     (add-rows! sheet data)
     workbook))

 ([sheet-name data & name-data-pairs]
  ;; incomplete pairs should not be allowed
  {:pre [(even? (count name-data-pairs))]}
  ;; call single arity version to create workbook
   (let [workbook (create-workbook sheet-name data)]
     ;; iterate through pairs adding sheets and rows
    (doseq [[s-name data] (partition 2 name-data-pairs)]
      (-> workbook
          (add-sheet! s-name)
          (add-rows!  data)))
    workbook)))
It seems that only 2 sequences can be passed

fabrao 2021-03-09T03:32:28.097700Z

or name-data-pairs can be many as I want?

fabrao 2021-03-09T03:45:41.098100Z

I solved using this ->

(-> workbook
          (add-sheet! s-name)
          (add-rows!  data))

fabrao 2021-03-09T03:45:49.098300Z

thank you anyway

seancorfield 2021-03-09T03:48:19.098700Z

I would never have suspected that was the answer to the question you asked 🙂

fabrao 2021-03-09T04:01:57.099700Z

🙂 the question was about generating sequences of string and arrays dynamic

seancorfield 2021-03-09T04:16:32.100600Z

Yeah, and you are not generating sequences here. You already had the data and you were asking about how to call a variadic function with particular pieces of that data.

yubrshen 2021-03-09T06:28:31.104800Z

I started a repl by clj -A:dev on the same machine (localhost) outside emacs when I use M-x cider-connect-clj it asks the port number. How can I figure out the port number? Or if there is a way to configure so that it will be started at a known port number? Try googled, but couldn't figure out. It seems that running a "socket server" is not for my situation, as M-x cider-connect-clj failed. I have cljs with shadow-cljs configured, the repl can be connected by M-x cider-connect-cljs with the configured port number. I can even using M-x cider-connect-clj to the same port for cljs repl, but apparently, I got another cljs repl, not the clj repl that I want. I even tried adding :nrepl {:port 5555} in deps.edn following the example of shadow-cljs.edn (with a different port number), but the connection still failed. Thanks for your help!

seancorfield 2021-03-09T06:42:33.105400Z

@yubrshen What is that :dev alias? Does it start an nREPL server?

seancorfield 2021-03-09T06:43:27.106100Z

I don't know of any tooling that would read :nrepl from deps.edn.

seancorfield 2021-03-09T06:43:44.106600Z

(what you said there doesn't sound like an alias)

yubrshen 2021-03-09T06:44:18.107Z

@seancorfield here is the :dev alias:

:dev {:extra-paths ["dev"]
                 :extra-deps  {org.clojure/clojurescript   {:mvn/version "1.10.520"}
                               org.clojure/tools.namespace {:mvn/version "0.3.1"}
                               thheller/shadow-cljs        {:mvn/version "2.8.52"}
                               binaryage/devtools          {:mvn/version "0.9.10"}}}

seancorfield 2021-03-09T06:44:57.108Z

Since that has no :main-opts, that isn't going to do anything but start a regular Clojure REPL.

yubrshen 2021-03-09T06:45:08.108300Z

Yes, the nREPL as a repl shell started, but I'm not sure it's a server.

seancorfield 2021-03-09T06:45:47.109200Z

I don't see anything in your alias that would start an nREPL server -- which is what cider-connect is going to need.

yubrshen 2021-03-09T06:46:14.110100Z

I just want to achieve the objective of having repl functionality by connecting to a repl started in a shell outside of emacs.

seancorfield 2021-03-09T06:46:16.110200Z

If you were actually starting nREPL, it would print out the port number it was using.

seancorfield 2021-03-09T06:46:55.110500Z

Are you working from a specific tutorial?

yubrshen 2021-03-09T06:47:17.110900Z

No, I'm not working from a tutorial.

yubrshen 2021-03-09T06:48:38.112400Z

You're right. I used to be able to observed the port number, when I did M-x cider-jack-in-clj, or even clj -A:dev but not anymore.

seancorfield 2021-03-09T06:49:35.113400Z

It looks like you're trying to use ClojureScript, not Clojure, and you're trying to use Shadow-cljs? The README for the latter seems to focus on starting REPLs using npx.

seancorfield 2021-03-09T06:50:12.114400Z

I don't know how you would use Shadow-cljs with the Clojure CLI. I have only used Figwheel Main with the Clojure CLI.

yubrshen 2021-03-09T06:50:41.115Z

I'm doing both clj and cljs following examples of Fulcro-3 tutorial. Indeed, I'm actually running the code of that.

seancorfield 2021-03-09T06:51:06.115400Z

Then maybe ask in #fulcro about using the Clojure CLI with it?

yubrshen 2021-03-09T06:51:22.115600Z

OK. Thanks!

jb recluse 2021-03-09T15:45:47.153800Z

i didnt see a question in #fulcro - did you get this resolved or do you still need assistance?

yubrshen 2021-03-09T19:19:39.157100Z

No, it's not yet resolved. I need to find time to investigate. Thanks! I doubt it related to Fulcro. I'll try to minimize the scope of my problem first.

pez 2021-03-09T07:40:27.117Z

I think CIDER has a command for jacking in to both clj and cljs with the same command.

yubrshen 2021-03-09T19:18:15.156900Z

I haven't tried it yet. Thanks!

Jan Ignatius 2021-03-09T08:28:58.121100Z

Good morning. I was stuck last night with figuring out how to pass attributes from the command line with lein run, the main issue being needing to convert the attribute to integer. I've made some progress but now I'm stuck at a different point: The code below runs in REPL, using the (now commented out) (-main "100") call, and I can see with the debug messages that the attribute is correctly extracted and converted to a integer. But when I try lein run 101 all the debugging messages come ok until the for statement - somehow lein just quietly stops there and the fizz-buzz-full function is never called.

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

(defn fizz-buzz-full [n]
  (println "fizz-buzz-full: arg received is " 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 arg-conversion [arg]
  ;; debugging
  (println "arg-conversion: received arg value is:" arg)
  
  (def i-arg (Integer/parseInt arg))
  
  ;; debugging
  (println "arg-conversion: arg converted to integer in i-arg:" i-arg)
  (println "arg-conversion: is i-arg is an integer? " (integer? i-arg))
  (println "arg-conversion: is i-arg is an string? " (string? i-arg))
  
  (for [x (range 1 i-arg)]
    (fizz-buzz-full x)
    ; debug
    ;(println "arg-conversion: (inside FOR) value of x is" x "and i-arg is " i-arg)
    )
  )


(defn -main [& args]
  (if (seq args)
  ; Foreach arg...
    (def first-arg (first args))
    (throw (Exception. "Must have one argument!")))
  
  ;; debugging
  (println "-main: arguments received from the command line: " args)
  (println "-main: the first-arg is: " first-arg)
  (println "-main: is the first-arg an integer? " (integer? first-arg))
  (println "-main: is the first-arg a string? " (string? first-arg))

  (arg-conversion first-arg))

;(-main "100")

Jan Ignatius 2021-03-09T08:31:25.121600Z

The output is as follows:

:~/test-world2$ lein run 101 102
-main: arguments received from the command line:  (101 102)
-main: the first-arg is:  101
-main: is the first-arg an integer?  false
-main: is the first-arg a string?  true
arg-conversion: received arg value is: 101
arg-conversion: arg converted to integer in i-arg: 101
arg-conversion: is i-arg is an integer?  true
arg-conversion: is i-arg is an string?  false

gon 2021-03-09T08:55:45.124100Z

for is not realized, it is lazy. you need to force it to "realize" its content, wrap it in a doall sexp like (doall (for [x (range 1 i-arg)] (fizz-buzz-full x) ....

Jan Ignatius 2021-03-09T08:57:28.124500Z

Ok, I need to read up on these concepts of lazy and realized.

👀 1
Jan Ignatius 2021-03-09T09:01:33.125400Z

And a thank you @gon - I've been at this for hours and I don't think I would have realized where the problem was on my own.

simongray 2021-03-09T09:09:32.126300Z

The laziness of sequences is one of the major gotchas for all beginners of Clojure.

Jan Ignatius 2021-03-09T09:40:06.126600Z

Is the range that makes the for lazy?

solf 2021-03-09T09:40:56.126700Z

It's for itself https://clojuredocs.org/clojure.core/for > ...and yields a lazy sequence of evaluations of expr...

Jan Ignatius 2021-03-09T09:42:03.127Z

Interesting. And when evaluating through REPL, these lazy sequences are always realized, even without the doall?

solf 2021-03-09T09:42:27.127200Z

eh, yes, because when you eval through the repl... the repl prints something

solf 2021-03-09T09:42:36.127400Z

to print it, it needs to realize it

solf 2021-03-09T09:43:14.127600Z

I've wasted a few hours myself on that specific issue, having something seemingly work on the repl, but not without it 😅

Jan Ignatius 2021-03-09T09:43:33.127800Z

I guess there is no linter that could catch that

solf 2021-03-09T09:45:05.128Z

Maybe in some specific circumstances, where lazy sequences wouldn't make sense... but not that I know of

raspasov 2021-03-09T09:48:36.128400Z

If you pass the lazy sequence around and use it eventually, it will get realized. Only case where you run into confusion is if you write something like:

(let [_ (for [i (range 10)] (println i))]
  (do
    ;... some other stuff
    ))

raspasov 2021-03-09T09:48:56.128600Z

This will not realize the sequence, because it’s never used.

raspasov 2021-03-09T09:49:18.128800Z

This will via (doall …) :

(let [_ (doall (for [i (range 10)] (println i)))]
  (do
    ;... some other stuff
    ))

Jan Ignatius 2021-03-09T09:51:34.129Z

The challenge I have is that I don't quite know how to read something as lazy. For me, in my code example, the arg-conversion function is called and it has a call for "for x until y, call another function (fizz-buzz-full) and include in that call the current value of the ongoing sequence between x and y".

raspasov 2021-03-09T09:55:47.129400Z

The docs are usually pretty clear about what returns lazy sequences: https://clojuredocs.org/clojure.core/map -> lazy https://clojuredocs.org/clojure.core/filter -> lazy https://clojuredocs.org/clojure.core/filterv -> returns a vector not lazy! https://clojuredocs.org/clojure.core/mapv -> again, vector, not lazy But I agree with your point, there’s no way to easily “know” at the beginning. If it’s bothering you, I’d even say just wrap all collection operations in (doall …) while exploring things at the beginning. Before long, you’ll develop the intuition what’s lazy and what’s not.

raspasov 2021-03-09T09:57:27.129600Z

I personally generally avoid (for …) in my code… It’s a macro, not a function. That’s awkward sometimes. I realize the temptation, coming from another language, because the word “for” is familiar. Try using map/filter/remove/reduce instead .

raspasov 2021-03-09T09:58:40.129800Z

(for …) has its uses and and it can be powerful, it can do cartesian products and stuff… https://clojuredocs.org/clojure.core/for but 98% of the time you don’t need that.

Jan Ignatius 2021-03-09T10:00:32.130Z

What would be a more elegant way for me to call the fizz-buzz-full?

raspasov 2021-03-09T10:01:48.130200Z

Try something like: (mapv fizz-buzz-full (range 1 i-arg))

raspasov 2021-03-09T10:02:56.130400Z

(intentionally using (mapv…) which is not lazy)

Jan Ignatius 2021-03-09T10:07:22.130700Z

It's interesting how the language keep tripping me. The mapv guide says that it applies f to the collection. Which in my mind reads (for my example) "applies fizz-buzz-full to <current vector created by range>" - which sounds exactly reverse what I want; to apply the vector value to the function...

raspasov 2021-03-09T10:15:32.130900Z

Perhaps it’s just the incompleteness of written/spoken language to describe abstract domain concepts… Maybe a more verbose wording is: calls f on each item of the collection and returns a vector of all (f item) return values.

Jan Ignatius 2021-03-09T10:28:58.132600Z

Talking about the docs, what exactly is "ISeq"? I keep seeing it in the descriptions

Aron 2021-03-09T10:46:48.133800Z

Hi, can someone please help me if there is an very short way to write a list of keys to be checked if all of them have truthy values in a given map?

Ben Sless 2021-03-09T10:53:43.133900Z

(apply every-pred (map (fn [k] (fn [m] (get m k))) ks))

Ben Sless 2021-03-09T10:55:09.134100Z

you can also use reduce instead of apply in this context

2021-03-09T11:05:07.136100Z

To me ”apply x” reads as “use function x (... on something)”, not “use value x with some function”. Not saying that’s the right way to read it but to me the sentence you mentioned does make sense

2021-03-09T11:05:38.136700Z

Talking in a general sense here, not Clojure-specific

raspasov 2021-03-09T11:09:22.137200Z

@email113 ISeq interface https://clojure.org/reference/sequences

Jan Ignatius 2021-03-09T11:10:52.137400Z

I feel like I need a new dictionary when reading these 🙂 .. e.g. what exactly is a "stateful cursor" in this context?

borkdude 2021-03-09T11:22:24.137600Z

This might also works

user=&gt; (every? identity (vals (select-keys {:a 1 :b false} [:a :b])))
false
user=&gt; (every? identity (vals (select-keys {:a 1 :b true} [:a :b])))
true

Aron 2021-03-09T11:28:33.137800Z

thanks, the first was how i imagined doing it but this should be an inline predicate, creating nested lambdas in such places is confusing for me, but creating a new utility dependency to put a new predicate is also seems a lot the second is what I imagined would be the short way, didn't know how to write it best, if I could use identity for it really

Aron 2021-03-09T11:28:36.138Z

thanks again

Aron 2021-03-09T11:29:19.138200Z

can't wait spec2 to be finished

➕ 1
Daniel Stephens 2021-03-09T12:11:27.138500Z

This has some caveats but they might be fine. Your map can't be nil and you want missing keys to be falsey:

(every? {:a 1 :b 2 :d false} [:a :d])
=&gt; false
(every? {:a 1 :b 2 :d false} [:a :b])
=&gt; true
(every? {:a 1 :b 2 :d false} [:a :missing])
=&gt; false

👍 1
borkdude 2021-03-09T12:21:51.138800Z

nice :)

Aron 2021-03-09T12:27:17.139Z

yes

Aron 2021-03-09T12:27:21.139200Z

all yes 😄

🎉 2
Sebastian 2021-03-09T12:34:49.142400Z

I've started working on clojure specs. I want to validate some data given in a json file. I want to validate this data before it goes into the database, so the error message happens further out than at the database level. I've created some specs and functions that validate now, however is there a way to test these like i would create tests for other functions in my test folder? Or am i misunderstanding how specs are supposed to work?

Sebastian 2021-03-09T12:35:58.143500Z

I would like to test the specific specs such as (s/def ::username (s/and seq string?))

Daniel Stephens 2021-03-09T13:00:26.143800Z

You can use s/valid? in your tests

(s/def ::username (s/and seq string?))
(s/valid? ::username "")
=&gt; false
(s/valid? ::username "asasa")
=&gt; true
(is (s/valid? spec val))

Sebastian 2021-03-09T13:02:44.144200Z

Yes, but these specs are in a different namespace. I am unsure how to handle that?

Bastien Rivière 2021-03-09T13:08:38.144400Z

You can specify the keyword with the namespace like this:

(s/valid? :my.namespace/username)

Bastien Rivière 2021-03-09T13:09:14.144700Z

Or if you have an alias:

(s/valid? ::myalias/username)

Sebastian 2021-03-09T13:12:30.145Z

I see. Thank you

Sebastian 2021-03-09T15:20:38.152500Z

I am having a hard time with optionality in spec and it seems im not alone. I am currently trying to express these predicates. Either :type1 is true or :type2 is true. If type1 is true then val also needs to be valid. If type2 is true then val can be optional or even nil. Is there a possible way to express this with specs?

(s/def ::type1 (= :type1 %))
(s/def ::type2 (= :type2 %))
(s/def ::val int?)
(s/def types (s/keys req-un [:type1]
                     opt-un [:val :type2]))
I tried modelling val into a map with type1 and type2
(s/def ::types (s/or :type1 (s/keys :req-un [::type1 ::val])
                     :type2 (s/keys :req-un [::bilag])))
However this does not include "val" in case of type2. And if i include val in type2 it will fail because it cannot be nil. Is there a way to better express this using specs, or is this out of scope of what spec is?

alexmiller 2021-03-09T15:23:33.152800Z

this is what s/multi-spec is for

🙌 1
alexmiller 2021-03-09T15:23:49.153200Z

https://clojure.org/guides/spec#_multi_spec

🙌 1
Sebastian 2021-03-09T15:35:20.153400Z

You are a saviour

2021-03-09T16:12:03.154600Z

For clojure.java.shell/sh, is it to get the output of the subprocess? The subprocess is long running and I want it to print out to the console.

alexmiller 2021-03-09T16:16:21.156200Z

Using Java’s ProcessBuilder api directly via interop is pretty easy and makes this pretty trivial (this api did not exist when sh was created)

lread 2021-03-09T20:13:31.158100Z

There is also @borkdude’s https://github.com/babashka/process which I personally like very much.

2021-03-10T15:00:11.200700Z

Great.

sova-soars-the-sora 2021-03-09T20:25:12.158600Z

is jubot the main clojure go-to for making a chat bot?

borkdude 2021-03-09T20:36:10.158700Z

@i :

(babashka.process/process ["foobar"] {:inherit true})

Savo 2021-03-09T21:00:24.160200Z

For fellow beginners & Regular 4clojure problem solvers. I wrote an article on interesting problem I encountered in 4clojure problem list. https://savo.rocks/posts/my-journey-to-understanding-function-composition/

butterguns 2021-03-09T21:34:20.160700Z

Hi. I enjoy using keyword args for optional function args. e.g.

(defn add 
  [a b &amp; {:keys [say-hello?]}]
  (when say-hello?
    (prn "hello"))
  (+ a b))

(add 1 2 :say-hello? true)
I come undone when I want to pass these optional args to a similar fn, e.g.
(defn increment 
  [a &amp; {:keys [say-hello?]}]
  (add a 1 ....what-goes-here?))
(Ignoring that I could use partial in the example above, because this is a trivial example). How do I do this elegantly? Or should I just give up and not use &amp; ?

seancorfield 2021-03-09T21:50:09.161400Z

Use a hash map instead of "named arguments" -- makes it easier to compose function calls.

seancorfield 2021-03-09T21:50:49.162200Z

Named arguments -- &amp; {:keys [...]} -- is OK for top-level functions that humans call, but it doesn't work well for functions calling functions.

seancorfield 2021-03-09T21:52:15.163800Z

Updating your code to take that approach:

(defn add
  [a b {:keys [say-hello?]}]
  (when say-hello?
    (prn "hello"))
  (+ a b))

(add 1 2 {:say-hello? true})

(defn increment
  [a opts]
  (add a 1 opts))

butterguns 2021-03-09T22:03:38.163900Z

Would you do this consistently, across your code? Or mix the hash map approach / named args approach depending on what you need?

seancorfield 2021-03-09T22:15:44.164100Z

I would use the hash map approach consistently, and only provide the named arguments version for a specific, human-focused use case, such as in the REPL.

seancorfield 2021-03-09T22:16:24.164300Z

It used to be common to see named arguments in a lot of Clojure code but pretty much everyone has moved away from it over the years.

butterguns 2021-03-09T22:19:45.164500Z

That's a shame, I love how elegant it is, but I can totally understand the reasoning behind that

seancorfield 2021-03-09T22:25:22.164700Z

It's only elegant when you're typing it in as a human 🙂 When you're trying to pass it down the call chain, it's a PITA 🙂

butterguns 2021-03-09T22:38:58.164900Z

Tell me about it 🙂

alexmiller 2021-03-09T23:02:00.165100Z

* yet! (stay tuned for 1.11)

😮 6
seancorfield 2021-03-09T23:07:22.165400Z

I was tempted to mention that discussion 🙂

seancorfield 2021-03-09T23:08:48.165600Z

Remind me: is the plan to allow a function declared with named arguments to also be able to take a hash map? So where you have &amp; {:keys [...]} you can pass a single hash map instead of the unrolled arguments.

seancorfield 2021-03-09T23:09:51.165800Z

Or will it work the other way round? Or will it work both ways? I think it's unambiguous in both directions, right?