Fork me on GitHub
#beginners
<
2020-08-07
>
Steiner01:08:34

in cider, how can I reset the repl ?

seancorfield01:08:37

@steiner3044 What do you mean by "reset"? (there's a #cider channel for deep dives on that)

Steiner01:08:10

reset all the variable and namespace, then restart

Steiner03:08:33

here in my src/book_clj, there is three file

➜  book_clj ls
book.clj  core.clj  server.clj
in core.clj, I want to require other two file, how do I do that?
(ns book-clj.core
  (:require [book-clj.server :as server])
  (:require [book-clj.book :as book]))
above content will throw an error

seancorfield04:08:06

(ns book-clj.core
  (:require [book-clj.server :as server]
            [book-clj.book :as book]))
That should work.

seancorfield04:08:14

What error are you getting?

Steiner06:08:44

here

clojure.lang.Compiler$CompilerException: java.lang.Exception: namespace 'book-clj.server' not found, compiling:(/home/steiner/workspace/school-work/book-clj/src/book_clj/core.clj:1:1)

Steiner07:08:59

eh, I seem to find the reason, here

(comment server
         {:books {login-id-1 book-1
                  login-id-2 book-2
                  ...}
          :storge-path storge-path})
I can't believe it won't be evaluate, but this below will be
(comment server
         {:books {login-id-1 book-1
                  login-id-2 book-2}
          :storge-path storge-path})
expression in comment isn't ignored

seancorfield16:08:19

@steiner3044 The contents of a comment must be valid Clojure forms -- since they are read (but they are not evaluated).

seancorfield16:08:33

... is not legal for the Clojure reader.

seancorfield16:08:17

If you want to write a placeholder for "more code goes here", you could use ,,, instead -- comma is treated as whitespace in Clojure.

seancorfield16:08:33

So you get the 'book-clj.server' not found because that namespace cannot be read by Clojure (because the syntax is not legal), so it can't be compiled -- and therefore a require of it elsewhere will fail with a compilation error.

seancorfield16:08:42

Hope that explanation helps?

Steiner00:08:31

yes, thank you

zackteo03:08:36

Hi guys, how do I tread my output in this such that I can use the apply str to join a resulting list of strings?

(defn sing
  "Given a start and an optional end, returns all verses in this interval. If
  end is not given, the whole song from start is sung."
  ([start] (sing start 0))
  ([start end] (for [v (range start (- end 1) -1)]
                 (apply str (verse v)))))
like (apply str '("test" "2test")) =>"test2test"

zackteo03:08:12

Ohhhhhh I got it I need to put (apply str to wrap the for

seancorfield04:08:19

@zackteo Using clojure.string/join is probably more efficient than apply str (and I'd say more idiomatic).

seancorfield04:08:07

You can also provide a separator: (clojure.string/join ", " ...) to provide a comma-separated list.

zackteo04:08:16

@seancorfield yeap i realised after looking at other solutions. I wanted to do (apply str ( interpose . Which I realise string/join does the same! Thanks tho :)

Klavs Klavsen06:08:27

Hi - I'm trying to follow https://grison.me/2020/04/04/starting-with-clojure/ - but when I run jack-in (just choose leinigen option) - it fails with "could not parse project.clj"

Klavs Klavsen06:08:17

and 0 hits on duckduckgo with: vscode calva "could not parse project.clj" - found google actually had hits.. trying those now 🙂

pez06:08:21

Can you describe the steps you take? When I follow the article, it works as described. And it is a deps.edn project, which makes the error message strange.

seancorfield06:08:41

@U0ETXRFEW The article talks about "jack-in" but never talks about setting up a Leiningen project and I suspect some of the IDEs' "jack-in" commands expect project.clj?

seancorfield06:08:26

(now I've read it, it seems like a fairly bad article to me -- it's rambling and vague and introduces all sorts of things it never talks about again)

pez06:08:48

Calva supports jack-in to CLI projects as well. I just tried to follow the article steps and it works as advertised.

seancorfield06:08:00

Good to know. Still a terrible article in my opinion 🙂

pez06:08:12

But since there is only a deps.edn file involved, Calva shouldn't be offering Leiningen jack-in. The error message is truly strange. Maybe if... I'll have to try a thing...

seancorfield06:08:10

The article shows an error message from clj-fmt referring to project.clj but not the same as the OP. That the article talks about Leiningen and shows its installation (twice!) and then doesn't use it, makes it garbage as far as I'm concerned.

pez06:08:12

No, that wasn't it. I tried renaming the deps.edn file to project.clj (why @kl should have done that, even with the somewhat cofusing article, I don't know, haha).

pez06:08:18

That produces an error, but not the error in the OP.

pez06:08:33

There should be better starter articles we could suggest.

Klavs Klavsen07:08:23

That would be great - I'm trying to run an existing project here

Klavs Klavsen07:08:44

so it works for everyone else (who uses emacs and repl typicly)

Klavs Klavsen07:08:13

and I totally forgot to respond in thread 😞 -The article is the one http://clojure.org links to for beginners

pez07:08:01

Ah, so an existing project. Then you could have run into a limitation in Calva. Assuming it is a Leiningen project. Calva tries to parse the project.clj file and for some files it fails. See if there is an eval reader macro in that file #=(...), iirc. I should fix that problem...

Klavs Klavsen07:08:26

my project.clj - seems to not contain such a character

Klavs Klavsen07:08:53

so some other parsing bug ?

Klavs Klavsen07:08:14

can I get the jack-in process to show some debug output

pez07:08:14

Yeah, Calva jack-in is not prepared for that kind of project.clj. What I would do is to start the project using lein and then connect Calva.

👍 3
pez07:08:13

Calva will need some dependencies loaded, that jack-in would have provided. When you start the project try using this command line:

lein update-in :dependencies conj '[nrepl"0.6.0"]' -- update-in :dependencies conj '[clj-kondo"2020.04.05"]' -- update-in :plugins conj '[cider/cider-nrepl"0.23.0"]' -- update-in '[:repl-options :nrepl-middleware]' conj '["cider.nrepl/cider-middleware"]' -- repl

pez07:08:10

Then when you get the nREPL server started on port ... message, you can connect Calva using the Leiningen project type.

Klavs Klavsen07:08:24

I started it with: lein repl - and it started fine ?

Klavs Klavsen07:08:52

and calva seems to have connected fine

Klavs Klavsen07:08:42

I can't seem to run: (go) + enter inside calva though

pez07:08:01

Yeah, but things like goto definition probably won't work. And the debugger will not have what it needs.

Klavs Klavsen07:08:20

I'll try to start it with your command then 🙂

metal 3
pez07:08:15

You'll need to use alt+enter (Evaluate top level form) with the cursor in or after the (go) call.

👍 3
Klavs Klavsen07:08:32

Thank you - now I'm running 🙂

Klavs Klavsen07:08:09

I still get a lot of reflection warnings from repl when starting though.. do I need to give repl more things to load or something?

pez07:08:36

Please don't hesitate to join he #calva channel and ask about using.

pez07:08:11

Reflection warnings are usually about Java versions. Not really Calva related.

Klavs Klavsen07:08:27

I'm using java 8 here..

pez07:08:53

See what your colleagues do about those warnings.

Klavs Klavsen07:08:01

I'll poke them 🙂

pez07:08:58

Wouldn't surprise me if they just disregard them. Haha.

Klavs Klavsen07:08:27

not unlikely.. I'll kick them/shame them into fixing 🙂

pez07:08:30

Where on http://clojure.org does the guide you used get linked, btw?

Klavs Klavsen07:08:22

No where it seems - I appearently remembered wrong in my confused state. Sorry 😞

Klavs Klavsen07:08:54

I'm glad I ended up getting it working 🙂

pez07:08:12

Haha, I'm glad too!

seancorfield06:08:42

@kl Sounds like your project.clj file is not valid -- can you share it here?

seancorfield06:08:38

That article doesn't seem to talk about project.clj -- the only mention seems to be in an error message when the formatter is run?

seancorfield06:08:31

Since it talks about CLI / deps.edn, I'm a bit surprised it suggests "jack-in" since that's often associated with Leiningen which the article doesn't talk about setting up a Leiningen project at all...

seancorfield06:08:48

...it seems like a very confusing article to me, to be honest.

Klavs Klavsen06:08:23

its the one http://clojure.org links to for beginners

Alex Miller (Clojure team)07:08:56

Can you point me to the page / link in question?

Klavs Klavsen06:08:54

any better one for explaining how to startup - using an existing project with vscode+calva? 🙂

Klavs Klavsen06:08:52

The code works for everyone else (who'se typicly using emacs)

Klavs Klavsen06:08:43

I usually use vim - but figured between intellij and vscode (the 2 covered in the article - vscode would be fine - so I installed codium build)

pez07:08:24

So if I have a directory starter and within that directory two files: deps.edn and playground.clj, with the following content: deps.edn

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}}}
playground.clj
(ns playground)

(+ 40 2)

pez07:08:02

Then with starter opened in VS Code, I choose jack-in. It gives me a menu of project types to choose from. I choose Clojure CLI. Then with the playground.clj file opened, I choose to load it. Calva: Load current file and dependecises. Then I can evaluate the (+ 40 2) form. With the cursor inside it, I press alt+enter.

pez07:08:33

If you do these steps. What happens?

Steiner07:08:08

hey, how can I write a data into a binary file, and then read it as binary data? in other way, how to serialize a data?

jamesleonis15:08:05

You want to check out the namespace. It interops with Java's readers and writers https://clojuredocs.org/clojure.java.io slurp and barf wrap the io/reader and io/writer fns respectively.

Jim Newton08:08:36

Is there an elegant way to dispatch within a function to an inline multi-function? Sorry that's confusing. What I want to do is the following:

(defmethod foo :some-key [& params]
  (dispatch params
    ([] ... );; handle empty params 
    ([a] ... ) ;; handle single arg, binding it to a
    ([a b] ...) ;; handle binary cases, binding them to a and b
     ...))
basically this is asking for some lower level interface to multi-functions interface which defn uses. perhaps something like the following would work, but is ugly.
(defmethod foo :some-key [& params]
  (apply (fn 
           ([] ... );; handle empty params 
           ([a] ... ) ;; handle single arg, binding it to a
           ([a b] ...) ;; handle binary cases, binding them to a and b
           ...) 
    params)))

Jim Newton12:08:49

There's the pattern I'm using. But it seems like a lot of useless coding.

(defmulti rte-expand
  "macro-like facility for rte" (fn [pattern _functions] (first pattern)))


(defmethod rte-expand :default [pattern functions]
  (invalid-pattern pattern functions))

(defmethod rte-expand :? [pattern functions]
  (apply (fn
           ([] (invalid-pattern pattern functions))
           ([operand] `(:or :epsilon ~operand))
           ([_ & _] (invalid-pattern pattern functions)))
         (rest pattern)))

(defmethod rte-expand :exp [pattern functions]
  (letfn [(expand [n m pattern]
            (assert (>= n 0) (format "pattern %s is limited to n >= 0, not %s" pattern n))
            (assert (<= n m) (format "pattern %s is limited to n <= m, got %s > %s" pattern n m))
            (let [operand (traverse-pattern pattern functions)
                  repeated-operand (repeat n operand)
                  optional-operand (repeat (- m n) `(:? ~operand))
                  ]
              (traverse-pattern `(:cat ~@repeated-operand ~@optional-operand) functions)))]
    (apply (fn
             ([] (invalid-pattern pattern functions))
             ([_] (invalid-pattern pattern functions))
             ([n pattern]
              (expand n n pattern))
             ([n m pattern] 
              (expand n m pattern))
             ([_ _ _ & _] 
              (invalid-pattern pattern functions)))
           (rest pattern))))

Jim Newton12:08:16

Isn't there a better idiom than (apply (fn (...) (...) (...)) args) ?

Ben Sless13:08:14

How about something like this?

(defmulti -expand (fn [_pattern _functions & args] (count args)))

(defmethod -expand :default [pattern functions & _args] (invalid-pattern pattern functions))
(defmethod -expand 2 [pattern functions n pattern'] (expand n n pattern'))
(defmethod -expand 3 [pattern functions n m pattern'] (expand n m pattern'))

Ben Sless13:08:57

you pass the outer pattern and functions as arguments instead of close over them, then dispatch on the length of the rest-args

Jim Newton16:08:11

hmm. the way I currently have it the defmulti returns a keyword such as :? or :+ or :exp or or or or. And each method (defined with defmethod) then might have a different function for each arity. some keys support multiple arities, some do not. as in my example above :exp supports arity 2 and 3, while :? supports only arity 1. some keys might support arbitrary arity. Currently it's up to each defmethod to implement the arities that make sense for the semantics of the keyword it is implementing.

Jim Newton16:08:51

BTW, how do you get a variable named pattern' ? that's really cool? is it a unicode character?

Ben Sless16:08:47

The idea with -expand is to use it inside rte-expand instead of the multi-arity apply

Ben Sless16:08:11

pattern' is just ascii characters, regular quote mark, can be part of symbol

👍 3
Ben Sless16:08:49

I should have written it like this, though:

(defmethod -expand 3 [_ _ n m pattern] (expand n m pattern))

Jim Newton17:08:20

That's really cool that ' can be part of a symbol. that's very un-lispy, but I love it!

Jim Newton17:08:17

clojure-rte.core> (let [a' 100
                        a'' 200
                        a''' 300]
                    (+ a' a'' a'''))
600
clojure-rte.core> 

Jim Newton17:08:25

just yesterday I wrote three local functions phi, Phi , and Phi-prime . I'll go back and rename them to celebrate this coolness

Jim Newton17:08:22

I was implementing a math formula where the reference material used ψ, Φ, and Φ ′.

Jim Newton17:08:52

I'm not sure whether it is a good idea to name these local functions using unicode characters???

Ben Sless18:08:31

' is an ascii character so it's not so bad, but I'd avoid it in general when naming functions or variables outside of lexical scope. phi Phi and Phi- look good enough imo

Casey10:08:47

Is there a recommended library for wrapping common math functions and constants (trig functions, pi, etc) for use in cljc files ?

dorab15:08:21

You might want to look at kixi.stats. Depending on what exactly you want, the whole library might be overkill, but kixi.stats.math might fit your needs.

Jim Newton10:08:52

Is there a better way to do this idiom which I have found myself doing several times in the past days?

(map (fn [_] operand) (range n))
I suppose (map (constantly operand) (range n)) is a bit better.

gon10:08:48

take a look at repeatedly

jsn10:08:51

@jimka.issy seems like a rather complicated way to do (repeat n operand)

👍 3
Jim Newton11:08:30

great. Thanks @jason358, For some reason I had convinced myself that repeat only worked for strings, and did n-many string concatenations.

jlmr15:08:10

When starting a repl using clj is there a way to break a long running evaluation using some keyboard shortcut?

jlmr15:08:48

My googling hasn’t found anything obvious, so I assume there isn’t…

ghadi16:08:50

there isn't a way to break long running evaluation

ghadi16:08:51

some evaluation environments like CIDER/NREPL stop threads preemptively using java's Thread.stop(), which has been deprecated for decades

ghadi16:08:36

the official way to do stop something on the JVM is to use Thread.interrupt(), but that is cooperative with the running code

ghadi16:08:54

(need to check the interrupted flag, or use an API that respects it)

ghadi16:08:08

but, TLDR, no, nothing generic. If you eval something like (loop [] (recur)) it's going to do that forever

jsn17:08:47

user=> (loop [] (recur))
^CExecution error (ThreadDeath) at java.lang.Thread/stop (Thread.java:853).
null
user=>
Ctrl-C works for me.

jsn17:08:03

Ah, but that's rebel-repl, not plain pure clj

ghadi17:08:30

Thread.stop is a bad idea

💯 3
jsn17:08:21

Well, obviously it's still much more convenient to have it as an option than to have none

💯 3
Alex Miller (Clojure team)16:08:12

can't say I've tried that in a long while but seems like it doesn't work anymore :)

sova-soars-the-sora16:08:09

I'd like to except one domain for cors (cross-origin CSRF) ... I'm trying to put the flags :access-control-allow-origin [#""] and :access-control-allow-methods [:post] on (wrap-cors ...) but it's not working...

sova-soars-the-sora16:08:19

still "invalid anti forgery token"

seancorfield16:08:54

@sova CORS and CSRF are different things. That error is not coming from CORS and you won't fix it by changing your CORS configuration. It means the submitting form does not contain the appropriate hidden field.

Shivam Shrivastava17:08:44

Can anyone help with using namespace keywords as enums? I have a namespace and was thinking to use keywords as possible statuses of an operation. There is an option to use namespace keywords. But i want "status" to be a qualifier too. For eg, if my namespace was my-app.ops , I plan to have possible status as :my-app.ops.status/status-1 , :my-app.ops.status/status-2 . This was possible if i had a separate namespace as my-app.ops.status, but i don't have one such. Does anyone have any opinion as how to proceed?

Alex Miller (Clojure team)17:08:36

you can just use those keywords - keyword qualifiers don't have to refer to an existing namespace

Alex Miller (Clojure team)17:08:06

user=> :i.made.this/up
:i.made.this/up

Shivam Shrivastava17:08:35

Yes, agree to that. I was looking for a way to leverage :: (something like ::status/status-1)

Alex Miller (Clojure team)17:08:54

you can't use :: unless you have an alias, which must refer to an actual namespace

Shivam Shrivastava17:08:33

yes. Just wanted to know what people normally do if they encounter this situation. I think using the full kw name is the way to go if this is required. Thanks for the response!

Alex Miller (Clojure team)17:08:34

this is an area we might work on in the future, it's a common request particularly in relation to spec

Alex Miller (Clojure team)17:08:22

you can kind of hack it by calling create-ns then alias but I find it's easier to just use the full kw name

👍 3
adnauseum17:08:47

Random noob question: how can I turn an int into a sequence of digits? If I were in Python I could cast a int to a string and iterate thru each character, parsing it as an int. Is there a way to do this in clojure? Context: a programming puzzle. Ppl rarely need to doo this normally 😄

Alex Miller (Clojure team)17:08:51

str will make a string from an int

Alex Miller (Clojure team)17:08:05

and strings are treated as sequences of characters by the seq fns

adnauseum17:08:18

Ah shoot, I didn’t notice that in the repl. Thanks!

sova-soars-the-sora17:08:38

@seancorfield is CORS a drop-in replacement for anti-forgery?

Alex Miller (Clojure team)17:08:57

(map #(Character/digit % 10) (str 123))

👍 6
adnauseum17:08:16

Thanks! 🙇 I did notice this in the Repl, but didn’t recognize that I was getting ascii chars which needed Character/digit

sova-soars-the-sora17:08:57

a 3rd-party API is hitting my server, there is no potential for a CSRF token ..I just have to whitelist one endpoint but it's not clear. seems like CORS would be the whitelisting for certain domains... still important to have anti forgery?

AC17:08:07

I usually do something like this -- (reverse (map #(mod % 10) (take-while pos? (iterate #(quot % 10) 12034)))) (no comment on efficiency or completeness, but worked well for me for advent-of-code)

noisesmith17:08:05

if you use reduce / cons instead of map you get the reversal for free

noisesmith18:08:34

(loop [result () pool 12034] (if-not (pos? pool) result (recur (cons (mod pool 10) result) (quot pool 10))))

noisesmith18:08:45

actually loop fits better than reduce, as there's no input sequence

Shivam Shrivastava17:08:03

@sova, i think that particular endpoint shouldn't be wrapped with csrf then. Instead, some other authentication mechanism can be used

sova-soars-the-sora17:08:29

so far i agree 100% 😃

sova-soars-the-sora17:08:23

but i'm not sure how to split my routes with run-server

ksd17:08:53

I am bumping into a rather puzzling error. I am trying to run a docker-compose command via clojure.java.shell to set up an admin user in a docker container automatically. If I run the following from Rich comment block, it works fine.

(sh/with-sh-env {"COMPOSE_INTERACTIVE_NO_CLI" 1}
    (sh/sh "docker-compose" "exec" "-T" "portal" "htpasswd" "-bc" "/etc/nginx/unryo/htpasswd" "admin" "a"))
However, when my script runs the same command it doesn't work. The bulk of my script is the following:
(let [cmds [["docker-compose" "pull"]
            ["docker-compose" "up" "-d"]
            ["docker-compose" "exec" "-T" "portal" "htpasswd" "-bc" "/etc/nginx/unryo/htpasswd" portal-username portal-password]]]
  (doseq [cmd cmds]
    (println cmd)
    (sh/with-sh-env {"COMPOSE_INTERACTIVE_NO_CLI" 1}
      (println (apply sh/sh cmd)))))

ksd17:08:19

portal-username and portal-password are properly defined and are strings btw

ksd17:08:30

when I define them as "admin" and "a" as above (println cmd) prints [docker-compose exec -T portal htpasswd -bc /etc/nginx/unryo/htpasswd admin a]

ksd17:08:13

oh and the (println (apply sh/sh cmd)) outputs {:exit 2, :out <htpasswd usage message>, :err } (readacted)

noisesmith18:08:12

could this have to do with the docker-compose command returning before the service is fully up and ready on the "up" step? I've typically seen a littering of sleep invocations in scripts that do multiple docker-compose commands for this reason

noisesmith18:08:00

also ProcessBuilder is not much more complex than sh/sh but a lot more powerful, if you ever need an OutputStream to poll as lines are ready instead of a string when the process exits, or an InputStream that can send inputs to the process as it runs, etc.

👍 3
ksd18:08:04

the full output of the "up" step is printed before the exec command is printed, meaning sh is done iiuc

noisesmith18:08:25

right, but "up" can return before the services it started are ready

ksd18:08:32

thanks, I'll definitely look into it

ksd18:08:12

when I run docker-compose up -d at the command line it blocks until it displays all containers being ready

noisesmith18:08:21

especially with java / clojure things, the "up" command says "your program started, I can exit" but that doesn't mean your service is going to handle a request in the next few seconds

ksd18:08:23

it would be pretty misleading if it showed them as ready when they were not

ksd18:08:44

also the thing I've tried running my script with the containers already up

noisesmith18:08:52

some containers (eg. kafka, clojure) will report being ready but not yet handle requests

ksd18:08:04

if I'm not mistaken in that situation up is essentially a no-op

noisesmith18:08:14

right, so that rules out that issue

ksd18:08:30

I got it

ksd18:08:32

silly mistake

ksd18:08:22

I was read ing my username, which was returning a symbol rather than a string

ksd18:08:01

I guess I should readLine from Java (I forget exactly what it's called) rather than read

sova-soars-the-sora18:08:21

(def application-routes 
  (routes 
  #'service-routes 
  (wrap-routes #'userland-routes wrap-anti-forgery)))
did the trick. (disable anti-forgery on everything, selectively re-enable on non-api routes)

Luca18:08:27

Hi I am trying to understand how I can use lazy sequences. I am writing a sudoku generator, where I generate 9 random rows at a time, I hope to filter according to some properties and then take the first nine valid rows as my sudoku grid. However I get eval timed out after few seconds, maybe lazy sequences are not meant for this problem?

noisesmith18:08:33

who times out?

Luca19:08:03

(defn sudoku-grid []
  (->> (repeatedly nine-valid-rows)
       (filter valid-columns?)
       (filter valid-blocks?)
       first))

Luca19:08:36

I am actually running figwheel

Luca19:08:08

Is there a difference between lazy-seqs in clojure and clojurescript?

dpsutton19:08:49

is it possible you never generate versions with valid-columns or valid blocks?

dpsutton19:08:52

if you aren't probing your search space well you could either be enumerating quite a few bad apples or even never getting into a valid grid

noisesmith19:08:53

this could be a simple algorithmic complexity question - if your guesses are naiive enough, you could run out of time before finding anything useful

noisesmith19:08:34

you could work out how many random guesses it would take to guess a valid sudoku, then see if there's anything about your guessing that could be informed by past results, or rule out whole subtrees of the problem that aren't worth trying

noisesmith19:08:10

lazy-seqs are slower than the equivalent loop, but only by a constant factor, so they won't be the source of this problem

noisesmith19:08:14

you could try adding (take 10000) between the repeatedly and the filters

noisesmith19:08:19

that should return very fast

noisesmith19:08:46

and likely establish that your first 10k guesses were all wrong

Clark Urzo19:08:55

So...I'm having trouble understanding a peculiar bit of code I wrote

Clark Urzo19:08:43

I'm trying to implement a REPL-driven workflow in the browser (cljs + shadow) by mounting components on a div

Clark Urzo19:08:56

(I know DevCards exists, but I kind of wanted to figure this out on my own)

Clark Urzo19:08:15

Anyway, long story short, I wrote mount so I can call it in the REPL:

(defn playground-dom [] (.getElementById js/document "playground"))

(defn mount 
  ([element]
   (d/render [element] (playground-dom)))
  ([head & elements]
   (let [node (reduce #(conj %1 (%2)) [:div (head)] (vec elements))
         f    (fn [] node)]
    (d/render [f] (playground-dom))
     )))

Clark Urzo19:08:40

My question is, why does this multi-arity definition work but this one gives me a not ISeqable error?

(defn mount 
  ([element]
   (d/render [element] (playground-dom)))
  ([& elements] ;; I removed the head!
   (let [node (reduce #(conj %1 (%2)) [:div] (vec elements))
         f    (fn [] node)]
    (d/render [f] (playground-dom))
     )))

Clark Urzo19:08:09

REPL messages:

> (defn x [] [:p "try"])
#'clojure-playground.core/x
> (mount x x x)
#object[Error Error: function clojure_playground$core$x(){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"p","p",151049309),"try"], null);
} is not ISeqable]

hiredman19:08:10

You did not remove the head

hiredman19:08:16

Sorry, I guess you did, I didn't realize that comment was referring to the earlier version

hiredman19:08:25

The error there is that varargs and fixed args can only overlap at 0

👍 3
hiredman19:08:57

(or 0 varargs I should say)

hiredman19:08:46

Basically you can't have arity overlap between varargs and not varargs

hiredman19:08:10

Why you are getting that terrible error message I dunno

Clark Urzo19:08:45

Ahhhh that makes sense

Clark Urzo19:08:14

I guess I could remove the 1-arity definition and make it fully general

Njeri19:08:57

Hi guys. I have a project generated by lein new reagent <app_name> . I’ve made an API call in the backend and have a handler to parse the resulting JSON.

(defn get-cards [req]
  (let [resp @(client/get "" options)]
    (if (= 200 (:status resp))
      (def cards-object (json/read-value (:body resp) (json/object-mapper {:decode-key-fn true}))))))
What’s the best way to pass cards-object to a cljs React component on the frontend?

Lu20:08:36

@njerigachoka hello! First of all, consider def as a const in js.. so you never want to use it in instances like that one. Instead, you want to directly swap your reagent atom and store the result of the handler in there. In other words you want to def somewhere in your code an atom, being (def state (reagent.core/atom nil))

Lu20:08:05

Then in your handler you do: (reset! state (json/....))

Lu20:08:30

And in your UI component you do @state to get the values out of it

Lu20:08:57

If this sounds a bit unclear, I can paste a more comprehensive example.

Njeri20:08:11

Yeah, this is a bit unclear. I’d appreciate the example.

dpsutton20:08:29

check out "Managing state in Reagent" https://reagent-project.github.io/ it should be very similar to that

Lu20:08:38

Writing it know 🙂

Lu20:08:18

This should be clear enough @njerigachoka :

Lu20:08:22

;; This is your function that I assume you want to call on-mount (or from an on-click handler)
(defn get-cards [req state]
  (let [resp @(client/get "" options)]
    (swap! state assoc :http-response
           (if (= 200 (:status resp))
             (json/read-value (:body resp) (json/object-mapper {:decode-key-fn true}))
             "Something went wrong!!"))))

(defn main-panel
  (let [;; This is a reagent atom. Every time a value in here changes, the views that
        ;; use it will re-render automatically (that's the react bit of it).  Usually,
        ;; you want to store your data in a map with several keys, which looks similar
        ;; to a JSON object.
        state (reagent.core/atom {:other-state-stuff "other stuff"
                                  :http-response nil})]
    ;; Anything happening before the (fn []...) function will fire only once, so this
    ;; space is great for your on-mount logic.

    ;; pass state to your function along with your other params
    (get-cards req state)
    (fn []
      [:div
       [:h1 "This is your UI component!"]
       [:p "To print the state values use @"]
       [:p (get @state :other-state-stuff)]
       ;; when nil, nothing will show in the UI
       [:p (get @state :http-response)]
       [:h3 "this is h3"]
       [:p "this is p"]])))

❤️ 3
Njeri21:08:32

@lucio or anyone else: so, the get-cards handler is in clj/app_name/handler.clj and my atom definition is in cljs/app_name/state.cljs . How do I reference the state atom in handler.clj? :require?

Lu21:08:14

You can do in any namespace (def state (reagent.core/atom {}), and then just require it as you guessed ;)

Lu21:08:24

Usually If you def your state, you want to use it for your whole app.. you DONT need to create other ones.. that’s the idea behind re-frame... but its also true that it depends on your use cases

Njeri21:08:15

I’m getting an error saying that state cannot be located on the classpath. How do I require a cljs file in a clj file?

seancorfield21:08:07

You can't. ClojureScript is going to run in your browser. Clojure is going to run on your server.

seancorfield21:08:40

Only the front end (ClojureScript, in the browser) can update that state atom.

Lu21:08:07

You want to set the state in the cljs code. When the server responds to the client, you can manage the response in the cljs code and set the state there.

seancorfield21:08:08

You have two separate "applications" really and they have to communicate via HTTP requests (or something similar). Does that make sense @njerigachoka?

👍 3
Njeri21:08:47

I think so. So if I’m making the API request in the backend, and updating state in the frontend, how do I pass the API request response to the frontend if I can’t update the atom?

Njeri21:08:24

Should I just make the API request in the frontend?

Njeri21:08:13

Ah. “They have to communicate via HTTP requests”. Got it. Thank you so much guys!

👍 3
Njeri20:08:56

Thanks, this makes more sense. What does calling “on-mount” mean? (Sorry, I’m a real beginner 😅)

Njeri20:08:32

Ah, that was a quick search away. Nevermind. Thanks again, @lucio

Lu20:08:58

Glad you got there 🙂 No problem! @njerigachoka

Clark Urzo21:08:33

btw thanks @hiredman kinda got distracted there for a sec

Clark Urzo21:08:37

this community is awesome

lpan22:08:03

What is the state of http-kit? Is it production ready? I am seeing ptaoussanis has been maintaining it.

noisesmith22:08:06

it's not broken, I don't think http-kit or aleph are in active development

lpan22:08:42

What is the recommended nio web server for Clojure?? It seems Jetty still only supports a thread per connection model

noisesmith22:08:47

I don't know if there's a great choice - both http-kit and aleph work right now

👍 3
noisesmith22:08:05

also, you can get very far before thread-per-connection is a problem

lpan22:08:27

True! Thanks a lot 🙂

noisesmith22:08:03

the http transport / thread layer is not the bottleneck in most clojure apps, and once it is you get more results with eg. nginx as a reverse proxy than you do with nio anyway

noisesmith22:08:32

if you use ring you can trivially swap out http-kit, aleph, or jetty

👍 3
seancorfield22:08:34

@lpan For what it's worth, we use Jetty very heavily in production (driving 40+ dating websites with a global customer base).

👍 3
seancorfield22:08:19

We swapped to http-kit at one point (due to a weird thread death bug in Jetty that later got fixed) but New Relic doesn't support http-kit so we swapped back to Jetty.

👍 3
lpan22:08:22

I am building an app that might handle potentially thousands of concurrent websocket connections. I thought maybe I should start with an nio-based HTTP server 😜

seancorfield22:08:39

We have a real-time chat server based on Netty directly, using Java interop, that's all based on concurrent websockets. My colleague knows more about it than I do tho'...

lpan22:08:08

I think Netty has the nio model 🙂 And irc alepth is the ring adaptor for Netty. Thanks a lot for sharing Sean!

seancorfield22:08:30

I'm pretty sure we chose Netty because we had a requirement for http://Socket.IO support and we ending up using Netty simply because it was what the server-side Java implementation of http://socket.io was built on...

seancorfield22:08:56

Yeah, my colleague just confirmed that: we had to support http://socket.io on the server and the most mature library appeared to be http://netty-socket.io so that's what we used, and that's why we use Netty 🙂

❤️ 3