beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Kevin 2021-04-22T00:59:34.344300Z

I see function names like put! or <! etc. What does the "!" exclamation mark mean? And when should I name my own functions with this character appended?

seancorfield 2021-04-22T01:21:49.344500Z

@kevin26428 https://clojure.org/guides/faq#qmark_bang

๐Ÿ™Œ 1
seancorfield 2021-04-22T01:22:45.345500Z

core.async has its own peculiarities around those functions too โ€” the single ! means non-blocking (must be used inside a go block/loop) and the double !! means blocking.

NoahTheDuke 2021-04-22T02:50:08.348100Z

in a macro, i have a let block where i put some expressions in a map. within the returned quasi-quote expression, i want to fetch one of those expressions from the map without resolving it, and then resolve it later. is that possible?

2021-04-22T02:57:22.348200Z

That doesn't parse for me

2021-04-22T02:58:00.348300Z

Something to keep in mind is macros usually generate code that does things, they don't do things

NoahTheDuke 2021-04-22T03:24:28.349300Z

let me think on how best to reword this, then. the solution i've found tonight is to wrap the expressions in a function, and then call that function when i need it, but idk if that's the best method

dpsutton 2021-04-22T03:40:43.349600Z

What are you trying to do?

2021-04-22T05:37:34.352200Z

I realise this depends a lot on my setup, but how do I deploy a Reagent app to Heroku?

2021-04-22T05:40:18.355300Z

I tried serving the index.html file of my app via ring-jetty, but it seems itโ€™s not that simple

phronmophobic 2021-04-22T05:40:47.356300Z

Is there a server side piece or is the app all client side?

2021-04-22T05:41:28.357200Z

I can already handle HTTP requests just fine and even serve handcrafted HTML files, but itโ€™s just blank otherwise

phronmophobic 2021-04-22T05:42:15.358400Z

So you have it working locally on your computer, but it doesn't work when you deploy?

2021-04-22T05:42:16.358700Z

@smith.adriane Ah, well there should be a server-side piece because of the database Iโ€™ll connect in the future

๐Ÿ‘ 1
2021-04-22T05:42:51.359900Z

Nope, not even locally. Hold on lemme post a couple of files

2021-04-22T05:43:47.360100Z

So this is my index.html file and normally it works when I run it with shadow-cljs

<!DOCTYPE html>
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
    &lt;link href="/css/style.css" rel="stylesheet" type="text/css"&gt;
    &lt;link rel="icon" href="<https://clojurescript.org/images/cljs-logo-icon-32.png>"&gt;
    &lt;title&gt;shadow reagent&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;noscript&gt;You need to enable JavaScript to run this app.&lt;/noscript&gt;
    &lt;div id="app"&gt;&lt;/div&gt;
    &lt;script src="/js/main.js" type="text/javascript"&gt;&lt;/script&gt;
    &lt;script&gt;window.onload= function() { client.core.main(); }&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;

2021-04-22T05:45:16.360300Z

My server on the other hand:

(ns server.core
  (:require [<http://clojure.java.io|clojure.java.io> :as io]
            [clojure.string :as str]
            [clojure.pprint :refer [pprint]]
            [ring.adapter.jetty :as jetty]
            [ring.middleware.reload :refer [wrap-reload]]
            [ring.middleware.resource :refer [wrap-resource]]
            [ring.util.response :refer [response 
                                        response?
                                        content-type 
                                        redirect 
                                        file-response 
                                        resource-response]]
            [reitit.ring :as reitit-ring]
            [server.routes :refer [router]]))

...

(defn resource-handler
  [request]
  (as-&gt; request r
    (:path-params r)
    (:filename r)
    {:status 200
     :body (io/input-stream (str "public/index.html" r))}))

...

;;;; Main
(def PORT
  "Heroku requires that the server uses $PORT.
  See <https://devcenter.heroku.com/articles/dynos#local-environment-variables>"
  (try 
    (Integer. (System/getenv "PORT"))
    (catch Exception e
      (println (str "Caught exception: " (.getMessage e))))))

(defn -main
  [&amp; args]
  (jetty/run-jetty (-&gt; resource-handler
                       (wrap-reload))
                   {:port (or PORT 3000)
                    :join? false}))

2021-04-22T05:46:06.360600Z

So when I run -main, it does serve the index.html file as I specified it

2021-04-22T05:46:33.360800Z

But it just displays a blank page, as opposed to when I run it with shadow-cljs where it displays the entire UI

phronmophobic 2021-04-22T05:47:46.361Z

I suspect the issue is areound (io/input-stream (str "public/index.html" r))

phronmophobic 2021-04-22T05:48:55.361200Z

it's probably producing a string like "public/index.htmlindex.html"

2021-04-22T05:49:25.361400Z

Ahh, hmm, but it does serve the HTML file as is, though? I checked the source in a browser and it does correspond to the contents of index.html

phronmophobic 2021-04-22T05:49:39.361600Z

(defn resource-handler
  [request]
  (prn "filename" (-&gt; request :path-params :filename))
  (as-&gt; request r
    (:path-params r)
    (:filename r)
    {:status 200
     :body (io/input-stream (str "public/index.html" r))}))

phronmophobic 2021-04-22T05:50:00.361900Z

ah ok

2021-04-22T05:50:09.362100Z

2021-04-22T05:50:11.362300Z

Ahhh hold on

2021-04-22T05:50:26.362500Z

could it be the case that I need to route /js/main.js as well?

๐Ÿ‘ 1
phronmophobic 2021-04-22T05:51:24.362800Z

I'm not sure what middleware is hooked up, but I wouldn't be surprised if there wasn't any data located at (-&gt; request :path-params :filename)

phronmophobic 2021-04-22T05:52:38.363Z

I wrote simple web app. Here's the route I use for serving files from the app's resources: https://github.com/phronmophobic/wavelength_helper/blob/main/src/wavelength_server/core.clj#L91

2021-04-22T05:53:51.363300Z

I see. I'll find a way to make it serve the entire public dir

๐Ÿ‘ 1
2021-04-22T05:53:56.363500Z

Thank you!

phronmophobic 2021-04-22T05:54:01.363700Z

also the routes for serving the html file:

(GET "/index.html" [] (response/resource-response "index.html" {:root "public"}))
   (GET "/" [] (response/resource-response "index.html" {:root "public"}))

phronmophobic 2021-04-22T05:54:46.364Z

using the built in resource-response and :root argument helps protect against serving other files not inside the "public" folder

2021-04-22T05:55:57.364300Z

I see. All right, I'll try that. I'm actually using reitit though but it should be straightforward to translate your approach.

๐Ÿ‘ 1
2021-04-22T07:28:31.367300Z

All right, so now I have a different problem. I am able to serve static files but for some reason my changes aren't reflected. It's like ring-jetty is caching my files on creation then just using those.

2021-04-22T07:28:55.367800Z

My question is, is there a way to disable this 'caching' behaviour?

2021-04-22T07:30:02.367900Z

The reason I think this is the case is because I tried deleting a file and yet both a cache-cleared Firefox and curl still return it

2021-04-22T07:30:23.368100Z

I have also rebooted, hoping that it would help, to no avail

2021-04-22T07:31:32.368300Z

In particular, recompiling with shadow-cljs produces a new public/js/main.js file, but those changes aren't reflected anywhere

roelof 2021-04-22T08:00:27.369100Z

@madstap thanks, I see it needed very complex code

Eric Ihli 2021-04-22T11:54:29.369800Z

Are the new versions of main.js named with their release version? (Either something you specified or their git sha.) If so, do the build tools have a way of updating your html files to point to those newly named files, or is that a manual process? If you have it set up to not use that release-version behavior and instead to always generate main.js exactly as spelled, then could you try changing that behavior and following the same deploy steps and see if that fixes it? If that fixes it, at least you narrow down that it's a caching issue in a post-deploy section of the process. I'm not familiar with Heroku so I don't know what cache-related behavior/solutions are there. I'm pretty sure something like a cache in ring-jetty wouldn't persist after a restart of the application. That would just be an in-memory cache, right?

2021-04-22T12:13:56.374100Z

Hi, is there a way to invoke a no-params function without using parenthesises, I mean execute a function by using another function? e.g.

(-&gt; #(println "hello") invoke)
Some type of invoke function? I mean I found I could use apply with nil,
(-&gt; #(println "hello") (apply nil))
but still Iโ€™m wondering is there anything built-in to execute no-params functionsโ€ฆ

yuhan 2021-04-22T12:16:31.374500Z

(defn invoke [f] (f))

2021-04-22T12:16:42.374800Z

Well there is nothing stoping you from writing ^that, Iโ€™m just wondering in what cases it would be useful ^^

2021-04-22T12:16:56.375100Z

(was about to type the same as yuhan ^^)

2021-04-22T12:17:15.375400Z

(defn invoke [xf &amp; args] (apply xf args))
Actually this does the job

yuhan 2021-04-22T12:18:17.376100Z

I think it's just trivial enough not to warrant being in clojure.core, heh

2021-04-22T12:21:15.376500Z

(-&gt; #(println "hello") (.invoke))

2021-04-22T12:22:16.376800Z

also works for multiarity functions as well (-&gt; #(println "hello" %&amp;) (.invoke 1 2 3))

๐Ÿ‘ 1
2021-04-22T12:52:22.377200Z

thanks

Edward Ciafardini 2021-04-22T14:26:19.380200Z

having an issue running planck on a localhost port & evaluating blocks in Atom. I use planck -n 777 to start the REPL, then "Connect To Socket REPL" in Atom- I get a success message initially, but when I try to evaluate a block, I get an error. Any ideas? (uploaded images follow the timeline of actions)

mfikes 2021-04-22T14:27:54.381Z

@esciafardini Maybe as a test separately try telnet 0 777 (or some other TCP client) just to be sure you can connect

Edward Ciafardini 2021-04-22T14:30:55.381100Z

getting similarly conflicting response... nc: connectx to localhost port 777 (tcp) failed: Connection refused Connection to localhost port 777 [tcp/multiling-http] succeeded!

dpsutton 2021-04-22T14:42:00.381300Z

can you try with a port greater than 1024? I think these ports require more privileges to use. Try with port 7777?

Edward Ciafardini 2021-04-22T14:43:18.381500Z

same issue on port 7777

Edward Ciafardini 2021-04-22T14:46:40.381700Z

**thank you for suggestion

dharrigan 2021-04-22T14:55:35.383Z

What would be a good construct to use as a "fire-and-forget" action to happen on another thread, i.e., I want to execute this function (that has side effects), but I don't care if it works or not, just go off and do it in another thread (as the side effect can take a few seconds to action)

alexmiller 2021-04-22T14:56:30.383400Z

future

dharrigan 2021-04-22T14:57:06.384100Z

right, my thoughts too, so I dont need to deref it ever...will the future be cleaned up once the function finishes?

alexmiller 2021-04-22T14:57:59.384400Z

Yes

alexmiller 2021-04-22T14:58:14.384700Z

No one will refer to it

dharrigan 2021-04-22T14:59:12.384900Z

w00t

dharrigan 2021-04-22T14:59:16.385100Z

perfecto, thank you ๐Ÿ™‚

dharrigan 2021-04-22T15:09:08.385900Z

Gotta say, that working with threads in this manner is so nice, Clojure makes it easy ๐Ÿ™‚

mfikes 2021-04-22T15:23:07.386Z

$ telnet 0 777
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
cljs.user=&gt; (+ 2 3)
5
cljs.user=&gt;

mfikes 2021-04-22T15:23:13.386200Z

^ This is what I get

mfikes 2021-04-22T15:23:36.386400Z

Perhaps local firewalls are in place on your box?

Edward Ciafardini 2021-04-22T15:29:00.386600Z

same here, must be an issue in Atom.

โžœ  ~ telnet 0 7777
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
cljs.user=&gt; (+ 2 3)
5
cljs.user=&gt; 
Everything works in Atom if I use this instead of planck: clj -J'-Dclojure.server.repl={:port,5555,:accept,clojure.core.server/repl}'

Franco Gasperino 2021-04-22T17:33:21.388800Z

Good morning. i have the a case where a spec returns false from a valid? call, but does not provide any context on what failed with an explain. I can provide details in a thread here if anyone is familiar with spec.

dpsutton 2021-04-22T17:35:55.389900Z

it's been years, but i think i remember a situation where using a function as a spec would fail when calling valid? but not when calling explain. I think the docstring was updated to reflect this rather than allowing functions in the spec. Let me see if i can dig it up

Franco Gasperino 2021-04-22T17:36:07.390Z

(s/def ::element-shape? 
  (s/and
   (s/map-of keyword? some? :count 7)
   (s/keys :req-un [::is-uuid?
                    ::is-name?
                    ::is-short-desc?
                    ::is-long-desc?
                    ::known-behavior?
                    ::is-config?])))

  (def mock-element
    {:id (str (random-uuid))
     :name "mock-element-1"
     :short-desc "Mock Input Element 1"
     :long-desc "This is a mock element which is intended to be used for event input consumption."
     :behavior :input
     :component :square
     :config {}})

(s/valid? ::element-shape? mock-element) 
  =&gt; false

 (s/explain ::element-shape? mock-element)
  =&gt;
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-uuid?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-name?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-short-desc?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-long-desc?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :known-behavior?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-config?) spec: :eventflow.ui.flow.elements.repo/element-shape?
nil

Franco Gasperino 2021-04-22T17:36:46.390600Z

replied to my self above with some truncated but illustrative example

Franco Gasperino 2021-04-22T17:37:55.390700Z

omitted all of the individual defined functions, but have confirmed the work individually and are evaluated in the repl

seancorfield 2021-04-22T17:39:31.390900Z

The explain is telling you that all those required keys are missing from the data โ€” and it is correct.

Franco Gasperino 2021-04-22T17:40:01.391100Z

oh crap! i missed the word "failed" in the output

Franco Gasperino 2021-04-22T17:40:12.391300Z

embarassed!

Franco Gasperino 2021-04-22T17:40:40.391500Z

too much coffee, or not enough

seancorfield 2021-04-22T17:40:41.391700Z

Your s/keys spec says that :is-uuid? etc should be keys in the data.

seancorfield 2021-04-22T17:41:14.391900Z

But the keys in your data are things like :id etc.

Franco Gasperino 2021-04-22T17:42:03.392100Z

yes, you're correct. Im only about 2 hours into the :: namespace macro use - still wrapping my brain around it

Franco Gasperino 2021-04-22T17:43:40.392300Z

let me back up my comprehension to a more basic point. If I have the following:

(s/def ::is-uuid?
  (s/and string? #(re-matches uuid-regex %)))

Franco Gasperino 2021-04-22T17:45:24.392500Z

the :: expands to the current namespace + local binding, no?

2021-04-22T17:48:51.394700Z

do clojure developers prioritize having just one data model in a system? is the idea that we'd chose the 'correct' data model for each component considered harmful?

raspasov 2021-04-23T12:18:07.415700Z

Fewer (ideally one) data models/data structures, more functions that operate on them

๐Ÿ’ฏ 2
raspasov 2021-04-23T12:20:21.416300Z

"It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." - Alan J. Perlis https://clojure.org/about/rationale

1
โœ… 1
2021-04-23T15:52:16.452200Z

@raspasov - fewer data models? or data structures?

2021-04-23T15:55:28.454900Z

i never thought of clojure as preferring fewer data models

2021-04-23T16:13:03.456500Z

i mean, sure, prefer as few as possible, but with data structures, clojure is quite opinionated and quite dogmatic - here are the handful you'll ever need. i never saw clojure as having a broad opinion about the different ways my application represents the concept of a domain entity

raspasov 2021-04-24T04:24:19.018300Z

I think if having multiple data models is justifiable, sure; when we say โ€œmultiple data modelsโ€ I understood it as having different data views/shapes onto the same model; perhaps I didnโ€™t understand the question well enough, apologies.

sova-soars-the-sora 2021-04-25T01:15:11.065500Z

Model is a highfalutin term with many abstract connotations. But in general I think you are both in agreement: creativity in how you access the data is unlimited / unbound / untethered, while the data itself is ideally only present once. (But consider the dish and the meal to be served.)

sova-soars-the-sora 2021-04-25T01:18:50.065700Z

@michael740 tell me more about the difference between data model and data structure. for example, does one data structure imply 1+ models on the data? And what does this distinction look like in code? ( if I have correctly observed the nuance)

dpsutton 2021-04-22T17:49:44.394900Z

you can evaluate ::is-uuid? in your repl and you'll see exactly what the reader expands it to. What do you mean local binding though?

dpsutton 2021-04-22T17:50:20.395100Z

qp=&gt; ::uuid?
:dev.nocommit.qp/uuid?

Franco Gasperino 2021-04-22T17:50:31.395300Z

thanks, that repl eval illustrated it for me

dpsutton 2021-04-22T17:56:40.395900Z

found it: https://clojure.atlassian.net/browse/CLJ-2111

dpsutton 2021-04-22T17:58:03.397500Z

qp=&gt; (require '[clojure.spec.alpha :as s])
nil
qp=&gt; (s/def ::vector vector?)
:dev.nocommit.qp/vector
qp=&gt; (s/valid? (s/coll-of any? :kind ::vector) [])
false
qp=&gt; (s/explain (s/coll-of any? :kind ::vector) [])
Success!
nil
`

phronmophobic 2021-04-22T17:58:21.397900Z

It almost certainly depends on what you mean by data model, but I think having good, idiomatic support for namespacing makes it easier for multiple "data models" to coexist. I think that's one of the reasons clojure has found strong adoption in data processing.

๐Ÿ’ฏ 1
Franco Gasperino 2021-04-22T17:58:38.398Z

thanks for that. my issue was with poor reading comprehension

phronmophobic 2021-04-22T18:03:44.398800Z

That doesn't mean you should create multiple data models if you can avoid it. It probably depends on the use case.

2021-04-22T18:24:20.399Z

I think https://www.slideshare.net/mtnygard/architecture-without-an-end-state paints a good picture of the world clojure is designed to thrive in, a world where there is no single system of record, and no single model

2021-04-22T18:25:07.399300Z

if you google around there are videos of the talk that goes with those slides too

๐Ÿ‘€ 1
๐Ÿ™ 1
marciol 2021-04-22T20:39:16.400700Z

@esciafardini there is a channel #chlorine-clover and @mauricio.szabo (chlorine's author) is usually active there.

Edward Ciafardini 2021-04-22T20:39:42.400900Z

awesome thanks

sova-soars-the-sora 2021-04-22T22:53:06.401500Z

I certainly try to achieve as much as possible with one canonical store of data, but just recently I invoked having 2 atoms instead of one for a dataset to ensure lookups would be fast if the datasets were to get enormously large. So in almost all cases I prefer one canonical representation / store of data, but some cases it's smarter or easier to flex provided you update them in a synchronous way. The potential to include bugs or glitches with multiple data representations is greater. There is the react adage "The View is a Function of the State" for the UI and the underlying representation in web programming -- and I think it applies on a lot of levels for computing and UI. Mainly that the methods playing on the data can be many, while the data ought be singular. Of course, it's not a hard-and-fast rule, and it makes sense to rely on whatever is best for the specific issues involved, but in general the fewer the stores of data, the fewer the inadvertent bugs due to out-of-sync'ness