Fork me on GitHub
#beginners
<
2019-09-16
>
caio.cesar.g.souza11:09:02

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

schmee11:09:50

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

schmee11:09:34

same as this (where m is the map):

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

caio.cesar.g.souza11:09:16

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

jaihindhreddy11:09:08

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.

jaihindhreddy11:09:15

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))

jaihindhreddy11:09:21

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

jaihindhreddy11:09:15

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)

jaihindhreddy11:09:46

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

caio.cesar.g.souza11:09:28

it is veerry cool! 😮

jaihindhreddy11:09:18

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]).

denisgrebennicov20:09:49

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

denisgrebennicov20:09:10

no can not, or no is not? 😅

denisgrebennicov20:09:43

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

alexmiller20:09:49

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

alexmiller20:09:17

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

denisgrebennicov20:09:29

but can be used like one, right?

alexmiller20:09:46

well, I'd say no

alexmiller20:09:59

types are about proving things ahead of time

alexmiller20:09:25

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

denisgrebennicov20:09:10

and if validation fails, a runtime exception is thrown?

alexmiller20:09:11

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

alexmiller20:09:35

answering that depends how you are using specs

andy.fingerhut20:09:04

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.

alexmiller20:09:14

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

alexmiller20:09:37

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

alexmiller20:09:01

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

andy.fingerhut20:09:11

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.

denisgrebennicov20: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

andy.fingerhut20:09:39

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.

andy.fingerhut20:09:25

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.

denisgrebennicov20: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

alexmiller20:09:52

I was surprised at how much Spectrum could do with specs

alexmiller20:09:10

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

andy.fingerhut20:09:13

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: https://github.com/arohner/spectrum

alexmiller20:09:34

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

denisgrebennicov20:09:40

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

christian.gonzalez20:09:59

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.

christian.gonzalez20:09:14

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

alexmiller20:09:17

close over an atom and count stuff

lorenzo.evans9421:09:35

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 http://galleria-app.app ns). What might I be missing/misunderstanding/doing wrong?

noisesmith21:09:44

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

noisesmith21:09:24

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

noisesmith21:09:08

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

noisesmith21:09:56

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

lorenzo.evans9421:09:04

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.

noisesmith21:09:32

then you are deploying the wrong jar I bet

noisesmith21:09:00

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

noisesmith21:09:49

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)

lorenzo.evans9421:09:41

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.

noisesmith21:09:17

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

noisesmith21:09:07

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

noisesmith21:09:37

@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

lorenzo.evans9422:09:29

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.

noisesmith22:09:45

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

noisesmith22:09:19

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

noisesmith22:09:51

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

lorenzo.evans9422:09:40

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!

noisesmith22:09:30

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

noisesmith22:09:28

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?"

noisesmith21:09:27

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