Fork me on GitHub
#beginners
<
2020-12-26
>
Grigory Shepelev05:12:54

It might be a stupid question. And it might me formulated incorrectly. But how do I run a channel in endless loop in my main function? I can do that in repl with (async/go-loop ...) but not with lein run

Grigory Shepelev06:12:09

Ok. I just figured out. It was not a problem of my channels misunderstaning, but wrong config.

Grigory Shepelev06:12:37

Now another question. A file being read in my code. How do I say it not to look for the same file to read after compilation, but rather have it "remembered"?

Timur Latypoff08:12:04

I think you could read the file inside a macro, and substitute the reading with actual values you read at compile time. Another approach would be to put the file you’re reading as a resource which is compiles as a part of the final JAR — so it’s always with you the way you compiled it.

Grigory Shepelev08:12:34

If I can somwhow pack it inside of jar?

Timur Latypoff08:12:13

Yes. With leiningen you do it by putting in resource/ folder in your project, and open the file with this function https://clojuredocs.org/clojure.java.io/resource

Grigory Shepelev08:12:15

yep. The provlem is I am doing exactly same thing by now with

(defmacro compile-time-read-file
  [resource-path]
  (slurp (io/resource resource-path)))

Grigory Shepelev08:12:52

ok. I got it.

👍 3
Grigory Shepelev07:12:25

Ok. I just get this. The problem is still with the first question.

Grigory Shepelev07:12:47

Having this function: (defn start [x y] (async/go-loop [] ...)) I want go make it work being into main :

(defn -main [& args]
  (start))
But it's not working as an endless loop.

Timur Latypoff08:12:05

In your example above, you’re not calling the start function, you’re returning the function object from -main. Wrap it in parentheses like that (start)

Grigory Shepelev08:12:26

Yes. I've meant. Sorry. I actually call it with parenthesis.

Timur Latypoff08:12:41

Ok. In this case, looks like what you need to do is to “wait” for the go-loop thread to finish from the main thread. You can do that by doing this: (async/<!! (start)) @UKG0SELQY

Grigory Shepelev08:12:15

Yep. This seems like the right approach.

👍 3
Timur Latypoff08:12:50

go-loop returns a channel from which you can read the result of async code execution when it’s ready (when the loop finishes). By reading for the value in a blocking way, we ensure that the main thread waits for the async loop to finish.

jacklombard11:12:19

I'm using next.jdbc to connect to postgres which is running as a docker container. I am able to connect to it using intellij and psql, but not via the repl. I have the following in my project.clj

[org.postgresql/postgresql "42.2.5"]
[seancorfield/next.jdbc "1.1.613"]
This is the code I executed to test
(comment
  (require '[next.jdbc :as jdbc])
  (def db {:dbtype    "postgresql"
           :classname "org.postgresql.Driver"
           :dbname    "foo-dev"
           :username  "foo"
           :password  "foo"
           :host      "localhost"
           :port      7432})
  (def ds (jdbc/get-datasource db))
  (jdbc/execute! ds ["select * from some-table"]))
I am able to connect to it via psql (entering the password on prompt)
psql -h localhost -p 7432 --username foo -d foo-dev
The error I get when I run (jdbc/execute! ds ["select * from some_table"]) is this
Execution error (ConnectException) at java.net.PlainSocketImpl/socketConnect (PlainSocketImpl.java:-2).
Connection refused (Connection refused)

jacklombard11:12:01

This is the docker-compose.yml part for the postgres container

db:
    image: postgis/postgis:13-master
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - 7432:5432
    environment:
      POSTGRES_USER: foo
      POSTGRES_PASSWORD: foo
      POSTGRES_DB: foo-dev

dharrigan11:12:12

Change username to user

dharrigan11:12:30

user=> (def db {:dbtype "postgres" :classname "org.postgresql.Driver" :dbname "foo-dev" :user "foo" :password "foo" :host "localhost" :port 7432})
#'user/db
user=> (jdbc/execute! (jdbc/get-datasource db) ["select now()"])
[{:now #inst "2020-12-26T11:25:58"}]

jacklombard12:12:44

still the same error 😕

dharrigan12:12:46

Interesting. Works for me, and I basically copy and pasted your code to my terminal

jacklombard12:12:41

Tried it in the older clojure.jdbc and it works, guessing it's a problem with next.jdbc and my system

(def idb {:classname                                  "org.postgresql.Driver"
          :subprotocol                         "postgresql"
          :subname                             "//localhost:7432/foo-dev"
          :user                                "foo"
          :password                            "foo"})

jacklombard13:12:31

Missed something important, had to use host.docker.internal instead of localhost since I was testing the port forwarded db connection. Works now, sorry my bad

dharrigan14:12:21

Glad you got it sorted! 🙂

dharrigan11:12:51

❯ docker ps                CONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS         PORTS                    NAMES
04d4199d1cd8   postgis/postgis:13-master   "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes   0.0.0.0:7432->5432/tcp   david_db_1

Joe11:12:41

Is clojure.lang.PersistentQueue still the appropriate data structure to use if you want a FIFO queue? i.e. conj to the back, pop off the front?

rakyi12:12:06

AFAIK that is its primary purpose

👍 3
noisesmith17:12:02

NB - don't ever use first / rest / etc. seq operations on a queue, it can quietly turn your fifo into a lifo

noisesmith17:12:31

only use into, peek, conj, and pop

noisesmith17:12:13

(and other tools built on those)

Andrew Byala16:12:32

Hey, everyone. I'm coming in from the Java/Kotlin world, and I'd like to try building a REST API using Clojure. In Java, you download Spring Boot and get to work, but I'm reading that in the Clojure world you tend to put together the pieces that you want, with everyone using Ring. Is there a de facto standard for building APIs nowadays? I I've seen samples with Reitit and Compojure; where is the industry heading?

noisesmith16:12:02

compojure and reitit are routers, ring provides an http server (which can be combined with the router of your choice). none of these are REST specific

noisesmith16:12:37

@abyala for "batteries included" opinionated setups, the biggest player is luminus, they have a project template generator which accepts args reflecting the setup you are looking for (eg. cljs webapp support, database backend...) https://luminusweb.com/ - the excellent book "web development with clojure" uses a luminus template, walks through building a full app, and is worth the price https://pragprog.com/titles/dswdcloj3/web-development-with-clojure-third-edition/

noisesmith17:12:57

but depending on what kind of REST API you are trying to make, there are tools that simplify that as well (and those can be combined with the big pile of code that luminus hands you)

Andrew Byala17:12:20

Awesome, I'll check that out. Thanks!

popeye17:12:03

Hello , When can we use apply and when can we use reduce? what is the difference between them? I searched in internet before posting here, But did not get good understanding!

Max Deineko19:12:14

To add to @U051SS2EU’s answer, I'll try and illustrate the difference: (apply f coll) will call f once with all elements of given collection supplied to it as arguments; (reduce f val coll) will call the function once for each element in the collection. Consider this:

(vector 1 2)                   ;; => [1 2]
(vector 1 2 3 4 5)             ;; => [1 2 3 4 5]
(apply vector [1 2 3 4 5])     ;; => [1 2 3 4 5]
(reduce vector 1 [2 3 4 5])    ;; => [[[[1 2] 3] 4] 5]
The short form (reduce f coll) is convenient when val is already in your collection:
(reduce vector [1 2 3 4 5])    ;; => [[[[1 2] 3] 4] 5]
So when your function yields the same results for (f (f x y) z) and (f x y z) the results of apply and reduce will be the same (as with e.g. addition), but generally they do different things. Last, consider this example of how reduce can be used with result and collection elements of somewhat different types:
(reduce #(apply update %1 %2) {:left 0 :right 0} [[:left dec]
                                                  [:right inc]
                                                  [:right inc]])
;; => {:left -1, :right 2}

👍 6
dgb2320:12:32

Can we say reduce is equivalent to apply if f is associative?

noisesmith20:12:35

that's the word I almost used earlier, yes

👍 3
Max Deineko21:12:38

Associativity is somewhat different, it is only defined for binary functions (but see below). And yes, reduce in its basic form [1] repeatedly applies a binary function to a sequence. If the function is associative, then the result will be the same for reversed sequence. But apply works with functions of different arities for which associativity makes no sense, e.g. (apply update [{:a 0} :a inc]). [1] https://en.wikipedia.org/wiki/Fold_(higher-order_function)

noisesmith21:12:00

right, and a foolish person could make a vararg function that is associative for two args, but does something non-associative for a higher arg count

Max Deineko21:12:22

Well, for higher arg count, the function cannot be associative or non-associative -- e.g. above (update :a inc) would make no sense Edit: I think we can translate associativity to variadic functions if we consider them as functions of one argument which operate on finite sequences, i.e. associative then meaning (using common mathematical notation) f((x1, f((x_2, ..., x_n)))) = f((f((x_1, ..., x_{n-1})), x_n)) for all such sequences. For apply and reduce to be equivalent on the other hand would mean that f((f((x_1, f(( ... )), x_{n-1})), x_n)) = f((x_1, x_2, ... x_n))._

noisesmith21:12:13

a version of plus that took three args would still be "associative" in the loose sense that you could start simplifying anywhere in the expression and get the same answer

noisesmith22:12:35

sorry, no, that's incoherent, clojure functions based on known associative functions are often varargs, and if so they usually do the same thing under reduce / apply, that's all

Max Deineko22:12:02

I think the reason apply and reduce seem similar is that clojure provides many functions with their reduce'd extensions to higher arities. Consider addition as commonly defined in mathematics: it is a binary operation which can be extended to sequences via formal equivalent of reduce. Since it's associative, it can even be extended to sets. And in many programming languages addition remains just a binary operation, and to get a sum of more than two numbers one has to reduce or fold it. It's just that clojure conveniently provides this already -- if we look at the source of +, we'll see

(defn +
  ;; snip
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
     (reduce1 + (+ x y) more)))
where reduce1 is specialized implementation of reduce afaics.

noisesmith22:12:05

I think it's because it's commutative that you can do that on sets

noisesmith22:12:10

(ins)user=> (apply / [1 2 3 4])
1/24
(ins)user=> (reduce / [1 2 3 4])
1/24

noisesmith22:12:36

I think I mixed up associative and commutative above myself

noisesmith22:12:50

@U9TGHG3LP reduce1 is a less efficient and less flexible version of reduce used before the clojure compiler has implemented reduce

👍 3
Max Deineko22:12:04

You're right, for the reduction to be well defined on at least some interesting sets, the operation has to both be associative and commutative. But I shouldn't have mentioned it -- we don't want to drift too far I reckon 🙂

popeye09:12:52

Thanks team for the reply, What I understood is apply and reduce both give same result, but acts different for different functions , and the way it handle functions are different

noisesmith18:12:31

@popeyepwr for many functions that take variable argument counts, adding an arg to the function is equivalent to another call - in that case apply and reduce do the same thing

noisesmith18:12:15

so "can" - for many vararg functions that treat their arguments homogenously, you can use either

noisesmith18:12:00

in practice reduce has some optimizations that the function might not, so it's often preferable

noisesmith18:12:10

concretely: (+ 1 2 3) will always be the same as (+ (+ 1 2) 3), so apply and reduce will do the same thing with + and [1 2 3]

noisesmith18:12:37

also conj

(ins)user=> (apply conj [] [1 2 3])
[1 2 3]
(ins)user=> (reduce conj [] [1 2 3])
[1 2 3]

noisesmith18:12:15

but with apply, it can be smarter about building the result, with reduce it needs to do more allocations (it builds a bunch of immutable vectors it only needs once)

noisesmith18:12:37

for correctness they both work, it is case by case which is more efficient

👍 3