Fork me on GitHub
#beginners
<
2023-07-12
>
Nim Sadeh02:07:04

What does :> mean? I see it a lot in ClojureScript React Native guides, but not sure if it's actually related to that

🐤 1
hiredman02:07:31

To clojure/clojurescript the languages it is just a keyword like :a or :b or :<

hiredman02:07:39

But that keyword is being used as part of the syntax of whatever dsl, I guess reagent

hiredman02:07:54

Apparently it is syntax for using react components in reagent

Nim Sadeh02:07:07

Ohh interesting, thanks

Reut Sharabani06:07:56

To my understanding it's used when you want to use a javascript component like material ui:

[:> muc/Tabs options [...]]
Where muc is imported like:
["@material-ui/core" :as muc]

Nim Sadeh18:07:09

Still a little confused about the syntax here - my understanding that per the linked doc, :> is shorthand for a function, but it’s called like [:> rn/Text "Text"], so an element in a vector. If it’s a function, shouldn’t it be called like [(:> rn/Text "Text")] ?

hiredman18:07:11

:> is just keyword

hiredman18:07:27

the meaning is given to it by the library that is processing the datastructure it appears in

Nim Sadeh18:07:08

Oh so it’s creating a data structure that Reagent knows “this keyword means that the next symbol is a JavaScript component”?

hiredman18:07:13

user=> [:> 1 2]
[:> 1 2]
user=> (type [:> 1 2])
clojure.lang.PersistentVector
user=> (mapv type [:> 1 2])
[clojure.lang.Keyword java.lang.Long java.lang.Long]
user=> 

Nim Sadeh18:07:14

When it’s compiling to JS

hiredman18:07:22

runtime more likely, not compile time, given the examples I've seen, but I am not sure

Nim Sadeh18:07:05

Is compile “take this CLJS and write a JS file”? wouldn’t that be the only time this would be possible?

hiredman18:07:25

the compiled output for something like (fn [] [:> 1 2]) is compiled for clojurescript, but in generally you can assume it will generate javascript code that when it is run will produce a function that when called will return a cljs vector where the first element is the keyword :> and the second is the number 1, etc

hiredman18:07:00

there is no reason other code cannot traverse and do whatever it wants with that datastructure

hiredman18:07:52

the cljs world is a little bit fonder of ridiculous macros, which is how you would handle this at compile time, so I won't rule that out, but with the examples I've seen there would be significant challenges to doing this all as a macro at compile time, so my guess would be the datastructure is run through an interpreter at runtime

joakimen15:07:31

How do you guys go about checking for nil in a series of calls (threading macro or let-chain)? From what I've found, it seems like several people are using custom macros for if-let/`when-let` to support multiple bindings, but considering how common it is to get something, then check that what you tried to get is actually there, I expected to find some kind of standard approach. Just checking for nil is obviously poor man's validation, but often it's enough. Take this example,

(if-let [a (op-1)
         b (op-2 a)
         c (op-3 b)]
  ;; success
  ;; error
  )
How would you go about achieving this, checking that a, b and c are not nil? Considered chaining the forms like (some-> (op-1) (op-2)) but wanted to check how you guys do this first. 🙂

pppaul15:07:20

if-letonly handles one binding

Ed15:07:06

I do sometimes use some-> for exactly this purpose.

1
pppaul15:07:11

(let [[a b c :as var-list] [1 3 2
                             ;;; (op-1) (op-2 a) (op-3 b)
                            ]]
  (if (not-any? nil? var-list)
    :success
    :not-very-successful
    )
  )

pppaul15:07:10

some->> some-> cond-> cond->> are nice for situations where they work

Ed15:07:32

I think he's intending that if-let works the same as let with an if inside. So I think

(if-let [result (some-> (op-1) op-2 op-3)]
  :success
  :fail)
is more what the op intends.

joakimen15:07:26

Being able to retain the individual bindings is nice, but not strictly necessary for my use case. Was also mostly curious how others are solving this, so thanks for the feedback, both 😄

alpox15:07:45

I didnt use this and have a quite vague understanding of monads but just lately came across this project: https://github.com/clojure/algo.monads where in tutorial 1 they do said task with

(defn f [x]
  (domonad maybe-m
    [a  x
     b  (inc a)]
    (* a b)))
Monads didnt seem to me much used in clojure though

👀 1
didibus16:07:15

Ya, I don't need to do this very often, but it's a shame if-let doesn't support more bindings

Sam Ritchie17:07:13

Yeah this pattern is encoded well with the “maybe monad” - that doesn’t mean you have to use the monad library but it’s a good source for a search term and validation that this is a familiar pattern!

pppaul18:07:58

elixir has a with statement that acts like if-let with multiple bindings, but it isn't conditional, if any of the bindings fail then there is some catch like statement for handling that. maybe something similar could be done in clojure, possibly more intuitive than if-let with multiple bindings.

Stephan Renatus18:07:50

heya. I’ve got two (more to come) scenarios for running a bunch of tests. So, I’d like to deduplicate them a bit. Right now, the two test cases have the same set of fixtures, but evaluated in different directories (`test/basic/` and test/not-basic/); operating on some standard set of files (docker-compose.yml and some service config). How could I get this sorted out? My first idea was to look into some sort of *file* magic to figure out the right config to use, but maybe that’s a silly approach altogether. (I noticed that the test runner doesn’t let me resolve *file*, it’ll be <expr>.) my setup is exactly that of https://blog.michielborkent.nl/babashka-test-runner.html, starting with the bb.edn paragraph.

borkdude18:07:01

you could maybe look at the value of *ns* ?

Stephan Renatus18:07:19

exec-a1bd4801-48c2-4151-96ae-50c63588de11
unless I’ve done it wrong. I’ve added a (shell "echo" *ns*)

borkdude18:07:49

that is the ns in which your tasks run, but not the ns in which your tests run

borkdude18:07:45

you want to determine the ns inside your fixture right?

Stephan Renatus18:07:48

yeah, so my idea would be that each of the test files imports some generic fixture, which in turn will use the files from the test’s directory. if that’s not completely unreasonable.

Stephan Renatus18:07:35

I guess I could “just” parameterize the fixtures, give them the working dir (or the config file path directly) as argument. I guess that would be more explicit?

borkdude18:07:56

being more explicit is usually best

Stephan Renatus18:07:42

OK I’ll just go with that. I think I should be able to figure that out myself. thanks!

borkdude18:07:47

let me know if you're stuck

Stephan Renatus18:07:44

(ns data-kafka-test
  (:require [clojure.test :refer [deftest is testing use-fixtures]]
            [babashka.http-client :as http]
            [babashka.process :as process]
            [test_utils :as util]))

(def dir "test/data-kafka")

(def messages "{\"id\": \"bob\", \"permissions\": [{\"path\": [\"cars\"], \"method\":\"GET\"}]}\n")

(defn publish-messages []
  (process/shell {:dir dir :in messages} "docker compose run -T kcat -P -b broker:29092 -t users"))

(defn with-messages [f]
  (publish-messages)
  (f))

(use-fixtures :once (util/with-dc dir) with-messages)
☝️ test code with this utility code 👇
(ns test_utils
  (:require [babashka.process :refer [shell]]))

(defn start-docker-compose [dir] (shell {:dir dir} "docker compose up --wait --quiet-pull"))
(defn stop-docker-compose [dir] (shell {:dir dir} "docker compose down"))

(defn with-dc [dir]
  (fn [f]
    (start-docker-compose dir)
    (f)
    (stop-docker-compose dir)))
seems to work fine. thanks for your help. would you cringe when seeing this in the wild? 😄

borkdude18:07:38

not at all, very nice

Stephan Renatus18:07:05

thanks for bearing with me. all that help! and it also helps to have a place where you (well, I) feel like no question is too basic or embarrassing. clojure-spin

borkdude19:07:07

glad you figured it out :)

Isaac Ballone20:07:14

Is it ill-advised to use the namespace part of a keyword (e.g. :ns\key) to store data if the namespace part doesn't actually refer to a Clojure namespace? For context, I'm building an api that has data that's always 1 level deep, so it would make sense for me to do :users/by-name , :users/emails, orders/count, etc. to access information. I have a feeling it's okay because clojure.spec and next.jdbc seem to do it, but I wanted to get some opinions

seancorfield20:07:54

What do you mean by "store data"?

seancorfield20:07:25

But in answer to the Q I think you're asking, qualified keywords do not need to match code namespaces.

seancorfield20:07:02

You can even use a long qualifier, like :.qualifier/my-key and say [this.is.my.qualifier :as-alias q] in your :require so you can use ::q/my-key in your code without needing a namespace of code for this/is/my/qualifier.clj

seancorfield20:07:31

(`:as` will load a namespace of code but :as-alias will not)

Isaac Ballone20:07:40

Sorry for being vague, I was just trying not to get too far into the weeds. I'm using CouchDB which structures data from db -> design -> views. So I was going to use the namespace part as the design name, and keyword name as the view name. Something like: (get-document db :design-name/view-name document-id) . I'm still not sure if I'm abusing the namespace part, or if I should just do`(get-document db [:design-name :view-name] document-id)`

Isaac Ballone20:07:53

It's good to know that there's no consequence for not matching code namespaces, and thanks for the tip about aliasing, I'll use it

seancorfield20:07:29

The main idea behind qualified keyword is to provide context and uniqueness: if you have :id or :name it doesn't tell you much and it will collide with any other unqualified :id or :name keywords; if you have :user/id or :account/name you can tell more about it -- and you can have :page/name and :account/name in the same hash map.

Isaac Ballone20:07:07

Sounds like it fits with my use case. I'll play around with it. Thanks! I appreciate the help

Abhi K23:07:51

Hi All, Is there a way I can combine these 2 collection passes into one, this code produces correct result but I am trying to reduce it to single pass.

(defn count-all-counties [coll]
  (let [key1-count (->> coll
                        (group-by
                          #(get % :key1))
                        (count))
        key2-count (->> coll
                        (group-by
                          #(get % :key2))
                        (count))]
    (+ key1-count key2-count)))

Bob B23:07:00

reduce over coll and throw the values into sets in a map or something, and then count the sets

thanks3 1
1
marcofiset23:07:20

(defn count-all-counties [coll]
  (count (reduce (fn [acc i]
                   (-> acc
                       (conj (:key1 i))
                       (conj (:key2 i))))
                 #{} coll)))
that would be something like this

thanks3 1
Bob B23:07:30

it might be desirable to keep the values separate, assuming that there could be overlaps in the values and they should be counted separately

❤️ 1
pyry06:07:10

clojure.core/frequencies might come in handy here as well, at least if you're okay with entries with nil values being discarded.