Fork me on GitHub
#beginners
<
2020-04-05
>
Oz00:04:02

@codonnell A ha! that's enlightening

codonnell00:04:46

Also, it's idiomatic to use kebab-case in clojure (which would make your event's name :get-tag-value)

Oz05:04:35

In this case it's an event that deals with a procedure call that happens on a remote, camelCased system, so it kinda makes sense :)

codonnell10:04:59

Fair enough! There are exceptions to every rule.

tjb01:04:27

another newbie question: so with compojure you are able to either deconstruct the request or get the whole request. what if i want to do both? in my use case i only want to deconstruct the :id from the URL and i need the whole request body. is there a way to do this?

tjb01:04:47

ex: (POST "/:id" req (println req)) <-- whole request but how would i access :id?

tjb01:04:15

ah! got it working by doing this (POST "/:id" [id :as req] (println id) (println req)) from a stackoverflow post but i am not sure why using the :as keyboard gives me both?

tjb01:04:56

very cool. i think my confusion comes from not fully understanding what is going on under the hood with :as

deep-symmetry03:04:00

:as is a common way of asking for a full value along with the values that have been destructured from it. In Clojure itself it works in let bindings and function arguments, and probably other places like them I’m forgetting at the moment. https://clojure.org/reference/special_forms#binding-forms

Oz05:04:56

I wonder why doseq doesn't trigger my re-frame event dispatch. I'm trying to do some mass dispatching like this:

(def tags-to-query
              (filter #(re-find #"ROBOT_1_" %) config/tag-names))
            (defn get-frequency [tag]
              (((@re-frame.db/app-db :tag-table) (keyword tag)) :frequency)
              )
            (defn request-tag [tag]
              (re-frame/dispatch [:auto-request-tag tag (get-frequency tag)]))

            (doseq [tag tags-to-query] (request-tag tag))
But nothing happens. Printing the tags works just fine though. Using doall also works, but I think it spikes my memory and CPU(especially in debug mode) because these are a lot of tags, and I don't see a reason to hold everything in memory.

didibus05:04:20

This seems re-frame related. Did you try adding a print inside the doeseq and printing "tag" to make sure that part works?

Oz06:04:28

turns out it was a mix of putting the code in the right place in the file, and getting the syntax right. And I ended up using dorun

(def tags-to-query
  (filter #(re-find #"ROBOT_1_" %) config/tag-names))

(defn get-frequency [tag]
  (get-in @re-frame.db/app-db [:tag-table (keyword tag) :frequency]))

(defn request-tag [tag]
  (re-frame/dispatch [:auto-request-tag tag (get-frequency tag)]))

(dorun (map #(request-tag %) tags-to-query))

didibus06:04:58

Ok, just so you know, doseq will work just as well as dorun, your problem most likely had nothing to do with it.

didibus07:04:34

And with your code, you can also use run! by itself:

(run! #(request-tag %) tags-to-query)

Oz02:04:48

Thank you! I'll look into it.

nick12:04:29

I wonder if there any simple rule or logic on how to define the most efficient thread pool size for java.util.concurrent.Executors/newFixedThreadPool (Executor Service). So far I've found this explanation on how pmap defines how many threads it uses: > * When the sequence is not chunked (for example subvec) the min parallelism is 1 and the max parallelism is (+ 2 N-cores). Example: with 12 cores, (doall (pmap #(Thread/sleep %) (subvec (into [] (range 1000)) 0 999))) keeps 12+2 threads busy. > * In case of chunked sequences (vast majority are size 32), the min parallelism is (min chunk-size (+ 2 n-cores)), while the max amount is equal to (+ chunk-size 2 N-cores). Example: with 12 cores, (doall (pmap #(Thread/sleep %) (range 1000))) keeps 12+2+32 threads busy. Do you think this could be appropriate to use the same rule/logic for defining java.util.concurrent.Executors/newFixedThreadPool pool size? https://purelyfunctional.tv/guide/clojure-concurrency/#executorservice

Jim Newton13:04:05

I've noticed that there are lots of functions like integer? ... they look like they were created to make type queries easier. They look very handy for the user, but not very easy to programmatically reason about. Does anyone know whether there is a declarative interface to these functions? What do I mean?

(defn integer?
  "Returns true if n is an integer"
  {:added "1.0"
   :static true}
  [n]
  (or (instance? Integer n)
      (instance? Long n)
      (instance? clojure.lang.BigInt n)
      (instance? BigInteger n)
      (instance? Short n)
      (instance? Byte n)))
The predicate integer? is intended to identify a set of objects which is the union of the types Inteter , Long , ... Byte . You can only know that by looking at the function or perhaps reading the documentation. However, a program cannot reason about this. Effectively all the objects which answer true to the integer? form a superclass of Integer, Long, clojure.lang.BigInt, BigInteger, Short and Byte. But since that type is not named, I cannot use it in a call to isa? .

alexmiller13:04:47

Correct. There’s no way to make such a type as that’s all locked down in Java

Jim Newton14:04:38

Yes, but even if there's no way to make such a type, there could feasibly be a reflective interface to the information in the implementation if integer? and friends. right?

Jim Newton14:04:20

Of course i could hack my own by reading the source code. How many such functions are there?

Jim Newton14:04:31

For example, the documentation for derive hints at this.

Establishes a parent/child relationship between parent and
tag. Parent must be a namespace-qualified symbol or keyword and
child can be either a namespace-qualified symbol or keyword or a
class. h must be a hierarchy obtained from make-hierarchy, if not
supplied defaults to, and modifies, the global hierarchy.

Jim Newton14:04:27

Although I admit that I don't understand how derive works, nor what a tag is

alexmiller14:04:19

derive works with keywords (the tags), not Java types

Jim Newton15:04:32

but the documentation for derive says it also works with class. I.e., the parent can be a class, but the child cannot (apparently)

Jim Newton15:04:22

until now java types seem to be classes. But i'm not a java expert, also I know less than nothing about java.

Jim Newton13:04:16

Another question about types: If I type Integer into the repl, it evaluates to some object whose printed representation is java.lang.Integer and whose type is java.lang.Class . My question is, given the symbol Integer how can I programmatically get the value java.lang.Integer ? Moreover, I'd hope that such an evaluation wouldn't be confused if the user happens to have a local lexical variable of the same name.

alexmiller13:04:00

The class object you get has a getName method which returns a String, which you could use or pass to symbol

alexmiller13:04:48

I dont think you can have a lexical local of that name. Or if you can, you shouldn’t :)

alexmiller14:04:46

resolve is a function to resolve Integer symbol programmatically if that’s a step you need

alexmiller14:04:26

I’m not sure if you’re looking class, string, or symbol

Jim Newton14:04:07

I think resolve is the function I need. (resolve 'Integer) returns the value java.lang.integer

Bill Phillips15:04:52

(resolve 'Integer) should be equivalent to Integer

Bill Phillips15:04:09

(unlike Integer, (resolve 'Integer) will not actually resolve Integer until the form is evaluated. but Java always has java.lang.Integer in the runtime, so this doesn’t make much difference)

Bill Phillips15:04:48

so to answer your original question (given the symbol Integer, how can I programmatically get the value java.lang.Integer?) the answer is: you already have it

Jim Newton15:04:58

I don't find the getName method on java.lang.Long

Jim Newton15:04:13

clojure-rte.core> (type 1) java.lang.Long clojure-rte.core> (getName (type 1)) Syntax error compiling at (clojure-rte:localhost:59681(clj)*:953:19). Unable to resolve symbol: getName in this context

Bill Phillips15:04:52

remember to add the .

Bill Phillips15:04:27

> (.getName Long)
"java.lang.Long"

Jim Newton15:04:46

clojure-rte.core> (.getName (type 1)) "java.lang.Long"

Jim Newton15:04:19

@U010GL90FN0, I don't know what you mean by, "the answer is you already have it"

Bill Phillips15:04:57

gascan.core> (.getName Long)
"java.lang.Long"
gascan.core> (.getName (resolve 'Long))
"java.lang.Long"

Bill Phillips15:04:24

Long and (resolve 'Long) are equivalent

Bill Phillips15:04:08

the only difference is that Long will resolve that reference when the source is parsed and loaded, and (resolve 'Long) will resolve it at the time (resolve 'Long) is actually evaluated at runtime

Jim Newton15:04:10

my question was given the symbol Integer, how to get the Integer class. For example.

(defn foo [x]
    ;; x is the symbol name of some class
    (resolve x) ...)
I don't see how I already have it, I have to call resolve to get it. right?

Jim Newton15:04:09

I have to resolve x at run-time, because I don't know its value at load time.

Bill Phillips15:04:24

ahh, then yeah. if all you have is a symbol name, then you have to resolve the symbol to find out what it refers to

Jim Newton15:04:32

indeed. thanks.

Bill Phillips15:04:44

that applies to any symbol. so your foo method would also work for map or type

Bill Phillips15:04:55

foo function, sorry. 🙂

teodorlu18:04:51

In this case, you could eval at runtime. Though I'd prefer resolve, as it makes your intent clearer.

user=> (eval 'Integer)
java.lang.Integer
user=> (resolve 'Integer)
java.lang.Integer

teodorlu18:04:23

Sidenote: I'd love to hear about your experience encountering Clojure, Jim. Not that many (that I'm aware of) come to Clojure from Common Lisp.

Nicholas Sorenson17:04:37

I'm not quite understanding the difference between commute and alter is commute guaranteed to eventually occur?

jsn18:04:07

AFAICS, the docs basically say that commute recalculates on commit, and alter doesn't. My understanding is that it means that alter causes the transaction to restart if the ref-ed value has changed under it, and commute doesn't, it just recalculates the new value and commits it.

jsn18:04:48

(I never used that stuff though, just read the docs)

Nicholas Sorenson18:04:19

Thanks. I'm going to think of it like atomic add versus atomic compare and swap. The operation must commute. You mostly do structured data in an atom I guess?

jsn18:04:51

Yeah, something like that. My guess would be that commute actually does compare-and-set though, but just for the commuted function, not for the whole transaction

Nicholas Sorenson17:04:06

Or is it more about order? Commute will happen but I don't care which order?

jtth19:04:48

So I’m trying to understand how to set up a web server from the ground up with all the things lein seems to do automagically, using tools.deps. Is there a good cookbook/example of how to properly wrap immutant in mount’s lifecycle? (this seems pretty straightfoward through defstate and calls to web/run(-dmc) and web/stop Should I care about mount vs component? Do these differ significantly in their affordances for something like tools.namespaces, which seems just for development namespace reloading? And finally: I’m trying to also set up nrepl and integrate it into tools.deps and Cursive. I have it loading, but it doesn’t load and start the -main function, which I guess is normal. Is the normal thing to do to define a user namespace (where? do i specify it or is it convention) and then start the server using defstates from mount manually in the repl?

seancorfield19:04:30

@jtth See whether this example helps https://github.com/seancorfield/usermanager-example/ -- it uses CLI/`deps.edn` with Component and can spin up http-kit or Jetty, but it would be easy enough to spin up Immutant instead.

jtth22:04:19

Whereabouts would I put wrap-reload in this example? I think it should go in my-middleware but I’m not sure how, given that it wants a var. Or perhaps somewhere in middleware-stack, but if I put it in the threading macro it doesn’t seem to do anything, again, I think because it wants a var? But if I wrap it like (-> blah (var) (wrap-reload) (var-get)) it doesn’t seem to work. Any tips are greatly appreciated!

seancorfield22:04:18

I've never used wrap-reload. I try to avoid all of the "magic" reload stuff.

seancorfield22:04:23

That usermanager example already uses vars when constructing middleware so that you can evaluate code into the running app without needing the "reload" stuff.

seancorfield22:04:59

The way I work (with all our web server processes at work) is that I start them from the REPL via my editor and then I can edit the code and just eval the updated function directly into the running process. No need to save a file. No need for a process that watches for changes and reloads files.

jtth00:04:46

ahhh i see. that’s probably better, even. thanks for the insight. that’s exactly the kind of thing i want to be aware of as an option, a process-wise change rather than blindly looking at some half-understood black box of technology. you’ve been really helpful. thanks!

codonnell19:04:21

Here's an example using mount (but http-kit instead of immutant): https://github.com/codonnell/mygiftlist-blog/blob/4bf305045319d923b11c9c7a8bdfd49753a0f110/src/rocks/mygiftlist/server.clj. Should be very similar with immutant.

seancorfield19:04:44

(I really don't like Mount because of the global singleton state -- I think Component is a more idiomatic Clojure approach)

codonnell19:04:02

☝️ I ended up converting that project to integrant for exactly that reason.

didibus22:04:38

You also have mount-lite and systemic which are similar to mount, but allow multiple systems to run more easily (though with some other caveats)

codonnell19:04:08

Specifically, I wanted to spin up a separate system of components for dev vs. test, and mount made that really hard.

jtth19:04:33

Ah, okay. I don’t think I understand enough about either to have an informed opinion but I have relied on your work before @seancorfield so I’ll probably just use Component because if you and tonksy use it it’s good enough for me.

codonnell19:04:45

About the user namespace, I think it is common to have it at dev/user.clj and include :extra-paths ["dev"] in a :dev alias in your deps.edn file.

jtth19:04:32

Ahh okay, I’ve seen something like that before but wasn’t sure where or how to express it. Thanks to you both!

codonnell19:04:04

The project I linked above does that; you can look at it as an example if you like. Personally, I like to put convenience functions to start/stop/restart/reload my system there.

jtth19:04:26

Also, I don’t really have a reason to be using immutant other than seeing it win some benchmarks and how it has a lot of batteries included. Should I narrow my scope and only worry about ring and jetty then?

seancorfield19:04:50

We use (embedded) Jetty in production and it's been solid.

codonnell19:04:13

I think ring and jetty is a solid choice.

teodorlu19:04:14

@ericnormand also recommended Jetty a few months ago: https://purelyfunctional.tv/mini-guide/clojure-web-servers/

jtth19:04:57

well that’s that then! thanks even more!

seancorfield19:04:59

At work we have a dev folder with some utilities, like starting a REPL with a Socket REPL and the Cognitect REBL UI, and we add them via aliases, like @codonnell suggests.

_rj_r_20:04:35

does anyone know of any projects that use sparkledriver, so some other web scraping library that opens a browser headless, clicks a button on the page to generate the dynamic HTML, then parses that? I need some real examples I can look at.. I've been through a couple libs, which I later figured out couldn't interact with the page, then I landed on sparkledriver and having issues figuring out a few things and have been googling for hours... any recommendation would be appreciated, either here or DM.. thanks