Fork me on GitHub
#beginners
<
2022-05-12
>
Hao Liang02:05:23

Hello, I have a leiningen project and I package it into an uberjar. I have some configurations which I want to modify them anytime without repackaging. Where is the correct place to put my configurations?

Bob B02:05:04

it's probably somewhat dependent on the deployment target, but an answer that aligns with the whole 'twelve factor app' thing is 'in the execution environment' - this might be environment variables or k8s config maps/secrets or a configuration file that's external to the build artifact

Hao Liang03:05:42

Thanks Bob. In fact It’s something like a desktop project and finally I ’d like to make native packages.

practicalli-johnny08:05:19

Operating system environment variables can be read by the packaged application (System/getenv) Although if there are a lot of configuration values, then using a Clojure config file and repackaging the application would be a more suitable option Packaging an uberjar should be a simple and relatively quick task, so if there is an issue slowing that task, then better to address that issue.

Hao Liang08:05:51

I got it, thanks John.

Benjamin08:05:25

what is the difference between read-string and clojure.edn/read-string ?

delaguardo08:05:42

first will evaluate what been read unless read-eval set to false, second - won't

👍 1
delaguardo08:05:43

that also mean clojure.edn/read-string can't read auto-resolved keywords: ::foo , ::user/foo

Martin Půda10:05:38

Difference between them is behaviour of #= (eval reader macro). There is no difference between (read-string "(println \"foo\")") and (clojure.edn/read-string "(println \"foo\")") . But compare that with (read-string "#=(println \"foo\")") and (clojure.edn/read-string "#=(println \"foo\")") .

xbrln09:05:08

Hello, am using https://github.com/jonase/kibit static code analyser and it suggested the following,

Consider using:
  (clojure.string/join content)
instead of:
  (apply str content)
Why is clojure.string/join preferred over apply str ?

delaguardo10:05:01

no reason at all. because without separator clojure.string/join does (apply str content)

delaguardo10:05:23

have you consider clj-kondo as a replacement for kibit? imho clj-kondo has much less opinionated set of rules and options to relax some checks either inplace or global

xbrln12:05:43

Thanks, i was running kibit with lein on git commit. I have clj-kondo in my editor (Idea), will check out how to set up clj-kondo on git commit.

Alys Brooks13:05:45

In theory, join communicates the intent more clearly, but not everyone is familiar with join . Also, it's probably overkill to pull in clojure.string for just this situation. Sometimes specialized versions are more optimized. Like @U04V4KLKC pointed out, join is actually implemented with apply and str so you're unlikely to see a difference.

👍 1
peter hull10:05:41

Just on this, is there a limit to the number of items in args when using apply? - i.e (apply f args) is the same as (f arg1 arg2... argN) so if args had 1000 elements would that be a function call with 1000 parameters?

xbrln09:05:07

It is one function call with all the arguments, but not sure about the limitation of arguments though.. When I try this it works without hanging, I think with a larger number you will get stackoverflow.

(apply + (take 1000000 (range)))

delaguardo09:05:28

apply itself has a performance penalty. In many case it will be faster to use reduce

user=> (time (dotimes [_ 10] (apply + (take 1000000 (range)))))
"Elapsed time: 1021.213365 msecs"
nil
user=> (time (dotimes [_ 10] (reduce + (take 1000000 (range)))))
"Elapsed time: 856.757079 msecs"
nil

peter hull11:05:19

@U025C8SL84T - interesting. I've found a reference that says the JVM is limited to 255 params at the very most (https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html#jvms-4.11) - I wonder how Clojure apply gets around this to pass 100000 args to + ?

delaguardo11:05:26

255 is a limitation to method parameters. however in clojure invoke method can take maximum 20 declaring that the last one must be a collection of Object

xbrln18:05:01

@U7KD4HQ1W, @U04V4KLKC Thanks for the inputs!! I do not know either why apply is able to call the function with more arguments than allowed...

peter hull19:05:14

It is as @U04V4KLKC says, up to twenty gets called like a 'normal' Java function, more than that will get 20 normal arguments plus an array of [N-20] Objects.

zakkor13:05:10

I am trying to use mount to manage the state for a datahike db connection:

(ns db
  (:require [mount.core :refer [defstate]]
            [datahike.api :as d]))

(def cfg {:store {:backend :file :path "/tmp/example"}})

(defstate conn
  :start (d/connect cfg))
In another namespace, I require conn and later reference it in a HTTP handler (not pictured)
(ns api
  (:require [datahike.api :as d]
            [db :refer [conn]]))

;; ...I use @conn below but it's too long to paste...
I run the server from the command line like this: clj -m api But when I try pinging the server using a HTTP request, I get this error: java.lang.AssertionError: Assert failed: (db/db? db)

zakkor13:05:18

the funny part is that it still works: I get a response from the API correctly, but I still get that failed assertion in the console...

👀 1
avfonarev14:05:53

For a while I've been thinking of contributing to the community by making some educational videos targeted at developers new to Clojure. My current two ideas are 1) examining the source code of some popular libraries and/or reimplementing their functionality, 2) writing some real life small size projects (such as a small Telegram bot for example). Which one do you think would be more valuable? I'm happy to hear any alternative suggestions as well.

dpsutton14:05:42

both sound great. blog posts or videos where you create something tangible is fantastic. “follow these steps and you will have a working telegram bot” is a great article. And after beginners can make a tangible thing, having an article thinking about libraries and how the code works helps their things be better

👍 1
dgb2315:05:39

I also agree that both sound great and I would watch them. I don't think you need to decide for one format and stick with it, especially if you're starting out. Try both (on the same channel) and give them catchy names, ask for feedback, see what sticks.

kennytilton16:05:54

I'd go with the small real life example. I remember when Reddit came out and someone on comp.lang.lisp reimplemented it in a couple of hours in CL. Only one of them ended up marrying Serena Williams. Hth!

😆 1
❤️ 1
uriel-caiado17:05:18

@U7ZSXG630 I'd love to read/watch something about implementing a telegram bot with Clojure, seriously. If I could, however, suggest it to be made in written format, I'd find it easier to follow.

👍 1
kennytilton22:05:13

Agreed on both reading and watching. The video of a well-rehearsed over-the-keyboard construction of sth non-trivial is a necessary grabber, accompanied by a well-tested written guide to doing it myself. Just include a disclaimer: Serena is taken.

Ted Ciafardini19:05:11

I would love to see some videos exploring existing libraries 👍

zakkor19:05:13

Fairly often I get random errors that get fixed by closing the REPL and evaluating the same thing again - but it's not because I messed up the state in my REPL, it seems to be located mostly in 3rd party libraries

; Execution error (IllegalAccessError) at datahike.datom/eval24858$loading (datom.cljc:1).
; combine-hashes does not exist
Is this sort of thing normal?

Alex Miller (Clojure team)19:05:55

by that description, no

Alex Miller (Clojure team)19:05:20

maybe if you shared more about your repl environment or usage, not sure where to go from that

hiredman19:05:43

my guess would be you are reloading code in a non-threadsafe way

zakkor09:05:18

Just an update but, it seems to be a problem in Datahike as I've found other people reporting similar issues about it here on slack

Jim Strieter21:05:16

I am using next-jdbc to query data from a postgres server. The following seems to work:

(jdbc/execute! (:datasource state) [(query-24-hrs epoch)])
It returns something like this:
[{:row #object[org.postgresql.util.PGobject 0x1ec0aaf4 "(1073053800,CCF,6.595)"]}
;; more results
]
I have 2 questions: 1.) I read the documentation and I get that the keywords are namespace qualified by default. However, when I look at the results, I do not see :my-namespace/:my-keyword or anything like that. Am I doing something wrong? 2.) I am hoping to get results like this:
[ {:epoch 1073053800
   :ticker "CCF"
   :price 4.595}
   ;; more results
]
How do I do that? I've tried adding {:builder-fn rs/some-function} to the function call and it hasn't changed the output.

seancorfield21:05:23

Always best to ask Qs like this in #sql as there's less traffic and they're less likely to get missed. That said, this looks like you might be using a computed expression in the SELECT that doesn't have an automatic type conversion -- since you're getting a PGObject back (a PostgreSQL Object).

👍 1
seancorfield21:05:26

next.jdbc can only provide qualified column names if they come from actual columns in tables -- computed expressions don't have that information (in JDBC).

seancorfield21:05:50

What exactly is that (query-24-hrs epoch) call producing?

seancorfield21:05:45

The row hash map looks like you're selecting a composite expression...?

Jim Strieter21:05:13

(query-24-hrs epoch) returns a string like this:

"SELECT (epoch, ticker, close) FROM bars_15_min WHERE epoch > 1073053800 AND epoch < 1073140200"

Jim Strieter21:05:55

Right now I'm sending the above string directly into jdbc/execute!. Is that the proper way to do this? Or do I need to use a SQL builder of some kind?

seancorfield21:05:48

SELECT epoch, ticket, close FROM ...
select the columns instead of a composite object.

Jim Strieter21:05:56

Do you pay any speed penalty if you do plain old recursion? (Not loop)

phronmophobic21:05:07

Typically, the main penalty you pay with "plain old recursion" is consuming space on the stack which limits the depth you can drill down. I think you would have to explain a little bit more about what you're trying to do for a more detailed answer.

👍 1
phronmophobic21:05:38

Generally, function calls are "cheap", but in some cases they're not. It really depends on what you're trying to do and how it compares to alternatives.

👍 1
phronmophobic21:05:25

If you're generally interested in performance, I think http://clojure-goes-fast.com/blog/ is a good clojure oriented resource.

ahungry23:05:10

recur for tco 🙂

Jon Olick02:05:44

recursion is generally slower than loops (at least in traditional programming anyway). The difference can in some cases be quite dramatic

quoll13:05:44

This may not be a perfect explanation (if I were at my computer I could check for specifics) but this is close. From a structural point of view: • recur will take any arguments and issue bytecode instructions to set the appropriate local values to them. Then it issues a goto instruction to return to the recur point (a function entry or loop instruction). • Recursion pushes all arguments to the stack, then calls the function. The function needs to pull those arguments back out of the stack into its local working registers. When done, the return value needs to be set and then function returning will update the stack pointer. So there are several more instructions. Now that I’ve typed that, I don't actually know what ClojureScript does for a recur. I need to look! If I had to guess, I’d say it generates a continue statement, probably with a label.