beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Grigory Shepelev 2020-12-26T05:54:54.455800Z

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 Shepelev 2020-12-26T06:38:09.456300Z

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

Grigory Shepelev 2020-12-26T06:40:37.457900Z

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 Latypoff 2020-12-26T08:17:04.472Z

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 Shepelev 2020-12-26T08:17:34.472300Z

If I can somwhow pack it inside of jar?

Timur Latypoff 2020-12-26T08:19:13.474100Z

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 Shepelev 2020-12-26T08:28:15.474400Z

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 Shepelev 2020-12-26T08:29:06.474600Z

w8 a second

Grigory Shepelev 2020-12-26T08:29:52.474800Z

ok. I got it.

1๐Ÿ‘
Grigory Shepelev 2020-12-26T07:56:25.458500Z

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

Grigory Shepelev 2020-12-26T07:57:47.459900Z

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 Latypoff 2020-12-26T08:09:05.462800Z

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)

Timur Latypoff 2020-12-26T08:09:56.463400Z

@altjsus

Grigory Shepelev 2020-12-26T08:10:26.463600Z

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

Timur Latypoff 2020-12-26T08:11:41.465900Z

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

Grigory Shepelev 2020-12-26T08:12:00.466200Z

1 min. I'll try

Grigory Shepelev 2020-12-26T08:13:15.467800Z

Yep. This seems like the right approach.

1๐Ÿ‘
Timur Latypoff 2020-12-26T08:13:50.468900Z

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.

Grigory Shepelev 2020-12-26T08:14:39.469200Z

thnx!

1๐Ÿ‘
Timur Latypoff 2020-12-26T08:09:05.462800Z

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)

Timur Latypoff 2020-12-26T08:09:56.463400Z

@altjsus

Grigory Shepelev 2020-12-26T08:10:26.463600Z

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

Timur Latypoff 2020-12-26T08:11:41.465900Z

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

Grigory Shepelev 2020-12-26T08:12:00.466200Z

1 min. I'll try

Grigory Shepelev 2020-12-26T08:13:15.467800Z

Yep. This seems like the right approach.

1๐Ÿ‘
Timur Latypoff 2020-12-26T08:13:50.468900Z

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.

Grigory Shepelev 2020-12-26T08:14:39.469200Z

thnx!

1๐Ÿ‘
Timur Latypoff 2020-12-26T08:17:04.472Z

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 Shepelev 2020-12-26T08:17:34.472300Z

If I can somwhow pack it inside of jar?

Timur Latypoff 2020-12-26T08:19:13.474100Z

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 Shepelev 2020-12-26T08:28:15.474400Z

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 Shepelev 2020-12-26T08:29:06.474600Z

w8 a second

Grigory Shepelev 2020-12-26T08:29:52.474800Z

ok. I got it.

1๐Ÿ‘
jacklombard 2020-12-26T11:11:19.478300Z

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)

jacklombard 2020-12-26T11:13:01.479200Z

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

dharrigan 2020-12-26T11:26:12.481Z

Change username to user

dharrigan 2020-12-26T11:27:30.481200Z

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"}]

dharrigan 2020-12-26T11:27:51.481400Z

โฏ 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

Joe 2020-12-26T11:31:41.482400Z

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?

jacklombard 2020-12-26T12:12:44.482500Z

still the same error ๐Ÿ˜•

dharrigan 2020-12-26T12:15:46.482700Z

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

rakyi 2020-12-26T12:20:06.482900Z

AFAIK that is its primary purpose

1๐Ÿ‘
jacklombard 2020-12-26T12:31:41.483100Z

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

dharrigan 2020-12-26T14:59:21.484200Z

Glad you got it sorted! ๐Ÿ™‚

Andrew Byala 2020-12-26T16:49:32.486700Z

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?

2020-12-26T16:56:02.488Z

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

2020-12-26T16:59:37.490300Z

@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/

2020-12-26T17:00:57.491100Z

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)

2020-12-26T17:05:02.491300Z

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

2020-12-26T17:05:31.491500Z

only use into, peek, conj, and pop

2020-12-26T17:06:13.491900Z

(and other tools built on those)

Andrew Byala 2020-12-26T17:16:20.493100Z

Awesome, I'll check that out. Thanks!

popeye 2020-12-26T17:25:03.494500Z

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!

popeye 2020-12-27T09:54:52.011400Z

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

2020-12-26T18:12:31.495600Z

@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

2020-12-26T18:13:15.496200Z

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

2020-12-26T18:14:00.497Z

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

2020-12-26T18:15:10.498Z

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]

2020-12-26T18:16:37.498400Z

also conj

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

2020-12-26T18:17:15.499200Z

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)

2020-12-26T18:17:37.499600Z

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

2๐Ÿ‘
2020-12-26T19:07:14.499800Z

To add to @noisesmithโ€™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}

2๐Ÿ‘
2020-12-26T19:22:36Z

Also, is this a known issue or am I missing something?

(ns rrb-test
  (:require [clojure.core.rrb-vector :as fv]))

(let [v (vector 1 2 3)
      t (transient v)]
  [(v 1) (t 1)])
;; => [2 2]

(let [v (fv/vector 1 2 3)
      t (transient v)]
  [(v 1) (t 1)])
;; => [2 nil]
This is with clojure 1.10.1, org.clojure/core.rrb-vector 0.1.2, zulu openjdk 11.0.9.1 -- I'd expect results above to be the same for vector and rrb-vector. I didn't find anything whch would explain this on jira or ask.clojure, and github page of rrb-vector claims it supports transient vectors, but since I've never done anything with transients yet, I'd rather ask before posting an issue for something trivial or known..

dgb23 2020-12-26T20:28:32.001100Z

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

2020-12-26T20:29:35.001300Z

that's the word I almost used earlier, yes

1๐Ÿ‘
2020-12-26T21:01:38.001700Z

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)

2020-12-26T21:06:00.002200Z

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

2020-12-26T21:15:22.002400Z

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

2020-12-26T21:32:13.003300Z

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

2020-12-26T21:51:23.003800Z

Not an issue that I knew about before. Looks straightforward to fix.

2020-12-26T21:52:13.004100Z

Be warned that there are subtle bugs that I do not know how to fix if you get into lots of subvec and catvec operations on core.rrb-vector vectors. Perhaps they are difficult to hit, but I would not use that library in anything that you were betting your job on.

2020-12-26T22:02:10.004400Z

Does this mean you can reproduce it or/and should I post it somewhere else? And thanks for the info -- and your help! :)

2020-12-26T22:04:35.004700Z

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

2020-12-26T22:17:02.004900Z

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.

2020-12-26T22:20:05.005100Z

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

2020-12-26T22:20:10.005300Z

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

2020-12-26T22:20:36.005500Z

I think I mixed up associative and commutative above myself

2020-12-26T22:21:50.005700Z

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

1๐Ÿ‘
FHE 2020-12-26T22:25:44.005900Z

@claudius.nicolae Thanks. The code above is a little hard to follow with my current level of familiarity. I do see re-frame in there though, whereas I have found myself being steered away from re-frame in favour of...something else I can't recall right this second.

2020-12-26T22:27:38.006100Z

I can reproduce it, and it appears I had started to work on it after someone else reported it a while back, but I had somehow forgotten about it. I will create a ticket for it in the JIRA system if there isn't one already.

1๐Ÿ‘1
2020-12-26T22:31:04.006300Z

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 ๐Ÿ™‚

practicalli-john 2020-12-26T22:36:09.007400Z

@popeyepwr I found this very useful https://stackoverflow.com/questions/3153396/clojure-reduce-vs-apply

3๐Ÿ‘
FHE 2020-12-26T23:02:48.008400Z

@abyala Thanks for that tip. I will now have a 2nd browser tab open to have that Slack tab open. Pretty insane for fairly new posts to be so buried by the UI though. ๐Ÿ˜•

FHE 2020-12-26T23:04:23.008600Z

@seancorfield Thanks. I signed up for Zulip, and I might start posting there instead since it might make a bit more sense. Still, though, after loading a lot of history in the beginners channel there I don't see any of the conversations I'm involved in. I don't see how it's possible for them to all be missing.

seancorfield 2020-12-26T23:50:13.009Z

This is a link to your message latest message: https://clojurians.zulipchat.com/#narrow/stream/180378-slack-archive/topic/beginners/near/220983040

seancorfield 2020-12-26T23:54:06.009400Z

@factorhengineering Are you perhaps looking at the beginners stream in Zulip, rather than the beginners topic within the slack-archive stream?