Fork me on GitHub
Caio Guedes11:09:02

Hey! I couldn't understand this line that I saw in a blog post... :thinking_face: (comp #{:root :todo-list :url-list} :type)


it will extract the key :type out of a map, and then check if the value matches any of the ones in the set


same as this (where m is the map):

(let [k (:type m)]
    (contains? #{:root :todo-list :url-list} k))

Caio Guedes11:09:29

hummm, I see!

Caio Guedes11:09:16

user=> (def m {:type :url-list})
user=>  (let [k (:type m)]
    (contains? #{:root :todo-list :url-list} k))
My tests on repl 😄


Keywords act as functions that look themselves up in an associative thing and return nil if they don't exist. And sets act as functions that returns the arg if it is present in the set and nil otherwise. comp is the mathematical function composition operator. Meaning (comp f g) evaluates to a function equivalent to #(f (g %)), assuming f and g are both of arity 1.


So in you're particular example: :type acts as the function (fn [x] (get x :type)), and #{:root :todo-list :url-list} acts as the function (fn [x] (if (contains? #{:root :todo-list :url-list} x) x nil))


And the composition is equivalent to: (fn [x] (if (contains? #{:root :todo-list :url-list} (get x :type)) (get x :type) nil))


This means keywords can be used as predicates to check whether they're present in an associative thing and are associated with a truthy value (not false and nil)


And sets can be used as predicates to check if a truthy value if contained within them.

Caio Guedes11:09:28

it is veerry cool! 😮


I can do (filter #{:x :y :z} [:a :x :y :z :b]) to get back (:x :y :z) but (filter #{:x :y nil} [:a :x nil true 1]) returns (:x) and not (:x nil). To get that, you have to do (filter #(contains? #{:x :y nil} %) [:a :x nil true 1]).

Denis G20:09:49

can/is clojure.spec used for compile-time checks?

Denis G20:09:10

no can not, or no is not? 😅

Denis G20:09:43

I was just thinking a little bit about it and it feels like clojure.typed on steroids

Alex Miller (Clojure team)20:09:49

well, I guess macro function specs are used during macroexpansion, so if you aot compile, you do see those at "compile" time

Alex Miller (Clojure team)20:09:17

spec is primarily a runtime contract system, not a type checking system like core.typed

Denis G20:09:29

but can be used like one, right?

Alex Miller (Clojure team)20:09:59

types are about proving things ahead of time

Alex Miller (Clojure team)20:09:25

specs are about validating things as they occur at runtime, and is predicate-based rather than type-based

Denis G20:09:10

and if validation fails, a runtime exception is thrown?

Alex Miller (Clojure team)20:09:11

and we recommend primarily using specs at test time and not using them at runtime

Alex Miller (Clojure team)20:09:35

answering that depends how you are using specs


One could imagine trying to use specs, or perhaps a restricted subset of them that did not execute arbitrary code, to do static checks of Clojure code, but the restrictions would be limited to what people know how to statically check in other languages, and perhaps even more restrictive due to dynamic nature of Clojure code.

Alex Miller (Clojure team)20:09:14

whether you're calling s/valid? or s/conform (don't throw) or s/assert (throws) or stest/instrument (throws)

Alex Miller (Clojure team)20:09:37

there is a project called Spectrum that uses specs for limited compile static analysis checks

Alex Miller (Clojure team)20:09:01

it can't completely check all specs or all cases though


That is just blue-sky imagination of what is possible given current knowledge, but I am not aware of anyone who is even thinking of trying to implement such a thing.

Denis G20:09:13

i’ve seen the clj-kondo work and it feels like this work could be extended in a sense for a better analysis whether or not this is lisp/clojure way of doing things is another story


I think it is definitely against the design goals of Clojure spec to restrict the power of writing specs to only those things that people know how to check them statically.


If someone goes to the trouble of recognizing a subset of specs as statically checkable, and statically checks them, that seems like an independent project from Clojure spec.

Denis G20:09:28

i’m not saying the Clojure spec project is responsible for it or that the team should put this as their new goal the question is merely whether or not this is possible and worth-striving for/yields any benefits

Alex Miller (Clojure team)20:09:52

I was surprised at how much Spectrum could do with specs

Alex Miller (Clojure team)20:09:10

it's an independent project, and out of scope for spec


I have read the README of Spectrum before, a while ago, and then forgotten of its existence until Alex mentioned it. Might be worth checking out if you are interested, and see what its capabilities are:

Alex Miller (Clojure team)20:09:34

there's a good talk on it from the conj a few years ago from Alan

Denis G20:09:40

thank you guys found the video. 2016. crazy that they’ve been doing this work back then already


Is there any good way of having a with-redefs fn return different values based on the number of times it’s been called? I want to test a function that should retry on failures.


I could store the number of function calls in an atom.

Alex Miller (Clojure team)20:09:17

close over an atom and count stuff


Would anyone have advice on solving a Heroku deployment error? I keep getting the following error: Could not find or load main class galleria-app.main, which is a a section of my procfile, posted in it’s entirety below. web: java $JVM_OPTS -cp target/galleria.jar galleria-app.main -m (based on the Heroku/clojure-getting-started walk through && repo). I tried clojure.main as it was written originally, but it was red underlined, leading me to change it based on my namespaces, which caused the red line to disappear. I tried to use gen-class/:gen-class to no avail w/ aot(?). Aside from the resources folder, target, project.clj etc, the only real app structure is src -> galleria_app -> app.clj (which has a ns). What might I be missing/misunderstanding/doing wrong?


seeing both galleria-app.main and -m is weird


typically you either do aot (so that you just specify the class of your main ns) or you use clojure.main plus the -m argument to specify the namespace to run


of course galleriai-app.main/-main is allowed to take -m some-ns as args, I just find that relatively unlikely


@lorenzo.evans94 what namespace actually implements your -main function? do you use aot?


I believe that would be (galleria-app).app, and I don’t have any :aot options in my :main in the project.clj file. I put the galleria-app in the procfile to see if perhaps the main that was being searched for was expected to be in something called clojure. But yes, I do have galleria_app/app.clj and app.clj has a -main function, but for some reason the deployment fails and says Could not find or load main class clojure.main if I use clojure.main.


then you are deploying the wrong jar I bet


your jar is a zip file - it should contain your clj files, plus the contentns of other jars you need (including clojure.jar)


a good editor can open a jar as if it were a directory, but you can also look inside it the same way you would a zip file in your os (may or may not require doing a manual rename)


Oooh, this is probably the noobest thing ever but do you have to “manually” generate jars? I was under the assumption jars were mostly for when you wanted to distribute an app so people can run it on their local machine. That being said though, thanks a lot for your help & time, I have another lead now, which should keep me busy & learning for a few days.


the command line you showed was for running a jar - if the app deploy process makes the jar for you, it still needs to ensure the jar you make includes the things you need


using a jar is usually the best way to run clojure in a "real" or production environment


@lorenzo.evans94 a common error here is that leiningen makes two jars (regular, and fat jar), and you can get the error you describe by using the wrong one


Aaah, thanks for clarifying jars for me. Update: switched java to lein and got it up and deployed in no time, so I’m going to stick with that for my toy web apps for now, but am also going to be more cognizant of jars.


using lein to run an app is kind of like using make to run other programs - it's a build tool that happens to be able to run your program too


for dev / testing that's totally legit, but I'd be skeptical of using it for real work


"what version of the app was running?" "hmm, whatever was in the file sysytem when lein was called I guess..."


So, basically, if I want to have/am writing a real world sized/production level app, java & co is the way to go to avoid headaches further down the road. Gotcha!


yeah - that way you can have a specific known artifact, lower startup overhead, etc.


I think the best rule of thumb is "if this app was written in c, would I be using a Makefile, or running the executable directly?"


if you have galleria_app/app.clj and app.clj has a function called -main, then clojure.main -m is correct