Fork me on GitHub
#clojure
<
2022-06-02
>
pinkfrog00:06:15

Hi. One question on deps exec-fn. I am invoking like this:

clj -X:dev tool.flyway/create-migration :name test
However, what the create-migration function receives is
(defn create-migration
  [& {:keys [name] :as _opts}]
  (prn _opts)
; {:name test}
We can see the test is a symbol instead of a string. Why is this designed so? and how to change this?

hiredman01:06:41

The arguments are all passed through read-string

hiredman01:06:06

Which is why it is :name not ":name"

seancorfield01:06:10

clojure -X:dev tool.flyway/create-migration :name '"test"'
Unless you're on Windows, in which case see the section on quoting arguments here https://clojure.org/reference/deps_and_cli#quoting

seancorfield01:06:22

Exec arguments are treated as EDN. You can pass a hash map directly instead of named arguments:

clojure -X:dev tool.flyway/create-migration '{:name "test"}'

seancorfield01:06:01

Does that help @i?

pinkfrog01:06:08

Thanks. Just a little bit cumbersome to type ‘’.

seancorfield01:06:02

Well, you need a way to pass Clojure data via the command line [1 2 3] or {:a 1 :b 2 :c 3} are "multiple arguments" so you need to quote those to get the shell to treat them as a single argument -- that's a shell thing, not a Clojure thing.

seancorfield01:06:29

And since EDN can have both symbols (like foo) and strings (like "bar"), you have to be able to pass those via the shell arguments too: clojure -X:what ever '{:sym abc :str "def"}' or clojure -X:what ever :sym abc :str '"def"'

pinkfrog01:06:02

Yup. I am switching to babashka tasks,

bb some-task :name test
seems everything is passed as string

seancorfield02:06:17

If you use a Clojure -main function, the arguments are all strings -- but then you have to parse those arguments if you need data.

seancorfield02:06:59

That's all bb is doing there. It's just strings, like -main, not data like exec functions.

Carlo09:06:40

How does the function clojure.repl/source-fn disambiguate between functions with the same name in different namespaces if it's just called like (source-fn 'f) ?

p-himik09:06:39

Same way resolve does it, because it uses it. You can't have two functions f in the same namespace - one will replace the other when you :refer to it. Unless you refer to one of them via a namespace or an alias.

Carlo09:06:01

that makes sense, thank you @U2FRKM4TW! 🙌

👍 1
Takis_19:06:20

hello, for sql dsl, i know mainly 2, sql-korma(looks like not active anymore) and honey-sql, both look nice to me, but i prefer when functions are like functions with parenthensis

Takis_19:06:26

(select users
  (where (or (= :username "chris")
             (= :email ""))))

seancorfield20:06:08

For the benefit of the channel, here's what you can do with HoneySQL that gets you closer to that "pure DSL" feeling:

user=> (require '[honey.sql :as sql] '[honey.sql.helpers :as h :refer [select from where]])
nil
user=> (-> (select :*)
           (from :users)
           (where '(or (= username "chris")
                       (= email "")))
           (sql/format))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=> (-> '{select *
             from users
             where (or (= username "chris")
                       (= email ""))}
            (sql/format))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=> (-> '{select *
             from users
             where (or (= username ?user)
                       (= email ?email))}
            (sql/format {:params {:user "chris" :email ""}}))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=>
The first form uses a mix of helpers (functions) and quoted forms. The second is a quoted data structure. The third is a quoted data structure with named parameters (so you can have values applied to the quoted form). The helper functions just build data structures (they're mostly wrappers around assoc plus some convenience logic).

😍 2
Takis_19:06:22

this looks very nice to me, is there a reason why on honey-sql functions are not like normal functions and there are like (at least i think so that they are like this)

Takis_19:06:25

(where [:= :foo.a "baz"]) 

Takis_19:06:38

the first is bad for some reason that i dont understand?

enn19:06:44

not necessarily bad, but requires either using a macro or quoting to prevent it from being evaluated as Clojure code

Takis_19:06:20

yes i know, i made a dsl for mongoDB to make mongodb queryable like clojure, and i did that

Takis_19:06:39

it worked, but i am not so experienced like other people here, so i am curious

Takis_19:06:36

this is my https://cmql.org/documentation/docs/#example1 dsl for mongodb so i wished we had something similar for SQL

seancorfield19:06:58

DSLs require macros and they don't generally compose as well when you're trying to do programmatic construction.

seancorfield19:06:37

HoneySQL has helper functions so (-> (select :foo) (from :bar) ..) works.

Takis_19:06:41

i know i saw this, maybe there are reasons that i dont understand but i would like all to be like clojure calls

seancorfield19:06:41

If you prefer (= foo bar) over [:= :foo :bar], HoneySQL allows quoted lists but you have to escape things where you want values resolved.

seancorfield19:06:03

See this example in the docs: Symbols can also be used, but you need to quote them to avoid evaluation:

clojure
(sql/format '{select [t.id [name item]], from [[table t]], where [= id 1]})
;;=> ["SELECT t.id, name AS item FROM table AS t WHERE id = ?" 1]

;; or you can use (..) instead of [..] when quoted to produce the same result:
(sql/format '{select (t.id (name item)), from ((table t)), where (= id 1)})
;;=> ["SELECT t.id, name AS item FROM table AS t WHERE id = ?" 1]

seancorfield19:06:18

You can't have non-Clojure semantics for (= foo 42) without making it all macros -- which don't compose well. Building data structures and transforming them (with sql/format in this case) is much more flexible and more idiomatic.

Takis_19:06:32

ok thank you, adding clojure like calls like optional , would be nice for me, but ok maybe i dont understand something

seancorfield19:06:56

It's not possible without using macros. Which don't compose.

Takis_19:06:16

yes i know i made a dsl for mongodb with macros

Takis_19:06:25

i used normal (= :field 10)

seancorfield19:06:39

Macros make a horrible API for programmatic usage.

seancorfield19:06:47

They're fine for humans, terrible for code.

Takis_19:06:38

i thought those also, but perfomance into generating a query i think its not important, but maybe perfomance is not the only problem

seancorfield19:06:50

The big benefit of HoneySQL's approach is that people can programmatically build the data structure, using any code they want -- normal Clojure code -- and then it gets transformed from data to SQL.

seancorfield20:06:01

It has nothing to do with performance.

Takis_20:06:31

thank you for information and honey-sql, i am not experienced enough to have good opinion, just macros looks nice in code

reefersleep11:06:26

I agree with Sean. Hiccup is another good example. Just simple, very regular data structures that are easily understood and which you can easily construct (and deconstruct!) using all of the relevant Clojure core functions. You are not limited to whatever has been considered in the macro. Additionally, the datastructure conversion function is, presumedly, much easier to understand than the macro, so it’s easier to maintain. It’s true that macros can look nicer in code, but the aesthetic difference very small compared to the payoffs mentioned above.

dpsutton20:06:12

Has anyone seen some recent json performance comparisons for Clojure? Interested to see how cheshire v. clojure.data v. jsonista v any others stack up in their current incarnations

domparry20:06:04

Yup. Chris did some comparison with all of them, plus his own lib charred (zero dependencies...)

dpsutton20:06:06

oh nice. do you happen to have a link?

domparry20:06:08

Kinked you in the thread. There's a link there. Otherwise I do have it somewhere

domparry20:06:26

It's superb.

seancorfield20:06:46

That graph sure is hard to read... Is clj-json meant to be clojure.data.json in that?

seancorfield20:06:41

My feeling is that, since the last round of (big) improvements in c.d.j, it's "good enough" in most cases -- and it's great to avoid Jackson. I haven't looked at charred yet, though.

seancorfield20:06:58

(avoiding Jackson means avoiding Cheshire and jsonista, as I recall)

seancorfield20:06:28

It's annoying that ring-json depends on Cheshire (and isn't up to date so you get an old version of Jackson that has CVEs 😞 )

dpsutton20:06:51

we’ve undertaken a lot of json parsing for inferring schemas from json columns in databases so i’m looking for wins even though some stuff could be good enough for other parts of the codebase

seancorfield20:06:08

For the benefit of the channel, here's what you can do with HoneySQL that gets you closer to that "pure DSL" feeling:

user=> (require '[honey.sql :as sql] '[honey.sql.helpers :as h :refer [select from where]])
nil
user=> (-> (select :*)
           (from :users)
           (where '(or (= username "chris")
                       (= email "")))
           (sql/format))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=> (-> '{select *
             from users
             where (or (= username "chris")
                       (= email ""))}
            (sql/format))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=> (-> '{select *
             from users
             where (or (= username ?user)
                       (= email ?email))}
            (sql/format {:params {:user "chris" :email ""}}))
["SELECT * FROM users WHERE (username = ?) OR (email = ?)" "chris" ""]
user=>
The first form uses a mix of helpers (functions) and quoted forms. The second is a quoted data structure. The third is a quoted data structure with named parameters (so you can have values applied to the quoted form). The helper functions just build data structures (they're mostly wrappers around assoc plus some convenience logic).

😍 2
Takis_20:06:08

thank you for showing the alternative ways

stuartrexking23:06:13

How do I specify a varargs for a byte[] with Java interop? I want to call this method specifically https://javadoc.io/static/redis.clients/jedis/4.2.3/redis/clients/jedis/UnifiedJedis.html#sendCommand-redis.clients.jedis.commands.ProtocolCommand-byte:A...- For the String… signature I could use:

(.sendCommand jedis ^ProtocolCommand command (into-array String args))
What about for the byte[]…
(.sendCommand jedis ^ProtocolCommand command <byte[]...>)

seancorfield23:06:57

Try (into-array (class (byte-array [])) data)

stuartrexking23:06:53

Thanks @U04V70XH6 I ended up using

(into-array  (Class/forName "[B") data)

1