Fork me on GitHub
#beginners
<
2021-05-05
>
Kenneth Gitere05:05:21

Hi guys! I'm still having a hard time fully understanding how to write Clojure macros. Are there any resources online for practising writing Clojure macros? I'm only learning about them so I don't have any examples of macros I've written incorrectly

dpsutton06:05:19

two resources: - clojure for the brave and true chapter on macros: https://www.braveclojure.com/writing-macros/ - http://grep.app search for (defmacro https://grep.app/search?q=%28defmacro&amp;filter[lang][0]=Clojure

πŸ‘ 3
zackteo06:05:34

Hi everyone, I understand I can use reduce to do (reduce + [3 3 2]) => 8 But how do I use reduce to get (reduce + [[3 3][3 2][1 1]]) => [7 6] Do I do something with apply ? :thinking_face:

dpsutton06:05:36

(let [pairwise (fn [x y] (map + x y))]
          (reduce pairwise [0 0] [[3 3][3 2][1 1]]))

dpsutton06:05:34

pairwise addition is just adding first with first, second with second, and then reduce that over the collection of pairs

zackteo06:05:04

Sorry am a bit confused. If I want to extend it to 3. Should I do (apply map + [[3 3 3] [3 3 3] [2 1 3]]) then? Don't really understand how reduce pairwise works

zackteo06:05:52

okay i think i still need to understand how apply works in such cases :thinking_face:

dpsutton06:05:58

table=> (let [pairwise (fn [x y] (map + x y))]
          (reduce pairwise [0 0 0] [[3 3 3] [3 3 3] [2 1 3]]))
(8 7 9)
works just fine. it's because of the way map works. (map + [3 3 3] [2 2 2]) will be ((+ 3 2) (+ 3 2) (+ 3 2)) which is pairwise addition. pairwise means in a tuple, to add, add each "position" in one tuple with the same position in the other tuple

dpsutton06:05:20

i'm not sure apply really gets you anything in this problem of yours

zackteo06:05:34

whoops, I think i somehow skipped over evaluating that snippet, yeah that works

dpsutton06:05:26

if you really want apply you could do (map #(apply + %) (apply map vector [[3 3 3] [3 3 3] [2 1 3]])) The inner apply will pair up the first elements of all the tuples, then the second elements, and the first apply will add them all

zackteo06:05:27

apply does seem to work too (apply map + [[1 2 3 4] [1 2 3 4] [1 2 3 4]]) ;; => (3 6 9 12)

zackteo06:05:09

apply helps to get rid of the surrounding [] i guess

dpsutton06:05:11

oh right. of course. sorry about that. i got blinded away from that solution

zackteo06:05:41

no worries! thanks for your help πŸ˜„

Tim Robinson11:05:44

Hi, I'm using "lein repl" and trying to find out how to do things like history search etc. I've followed the trail from nRepl to https://github.com/trptcolin/reply which claims it has "history navigation and search and much much more" but doesn't contain any user guide. it mentions JLine but that doesn't seem to have a user guide either. can anyone point me at some documention to get me started?

flowthing11:05:55

I think lein repl uses readline if you have it installed, so history search specifically should be Ctrl+R. Obligatory "prefer evaluating things from your editor instead of typing into the REPL" caveat applies here, though. πŸ™‚

Tim Robinson12:05:41

Thanks - the manual page for readline does include some user instructions so that's very useful. I guess a lot of this would be obvious to someone who doesn't use set -o vi in bash :-)

FHE12:05:02

So I'm trying to get started with shadow-cljs, and there seem to be 3 options for dependency management: shadow-cljs itself (using shadow-cljs.edn), leiningen (using project.clj), or deps.edn (tools.deps is something related but I don't understand it at this point).

FHE12:05:38

Is there a good argument for using any of the 3 options over the others? Actually I really don't want to add yet another tool, so Leiningen is probably out for me.

gon15:05:28

shadow-cljs is not for dependency management, it is for ClojureScript, lein and tools.deps are for dependency management

FHE00:05:57

Even though there's a shadow-cljs.edn file? OK. Thanks. I'll start with just that and maybe try moving to tools.deps (or is that deps.edn??) later.

FHE12:05:12

I'm guessing using shadow-cljs.edn will be a bit 'smoother', but using deps.edn will give me more well-rounded practice (since I would be using I would be able to use any future projects without shadow-cljs).

Ryan12:05:13

Hey all, still getting my feet wet and had a question about the most clojure-y way to approach something: I have a big map for various settings for authentication, e.g. endpoints, urls, token settings … when I’m actually using the map, I want there to be some sprucing e.g. combine the endpoint and url suffix into one field.. is the idiomatic way to do that to basically have the map, have a function that transforms the map, and always invoke the map by calling the function instead? Right now I just have the map, and the code that consumes it is very verbose e.g. :url (str (get-in map :auth :protocol) (get-in map :auth :baseURL) (get-in map :auth :urlPort) (get-in map :auth :urlSuffix)) Any clues would be appreciated πŸ™‚

Tim Robinson12:05:41

as long as the map doesn't change, can't you pre-process it when you read it in (or whenever the namespace loads if it's hard-coded)? if you really need access to the 'raw' version as well, you could just expose both

βž• 3
πŸ‘ 3
Ryan12:05:15

Thanks for the response, I don’t think I’d need access to the raw version, but we might want to change the map at some point via repl or other dynamic mechanism, but I suppose it would be as elementary as re-running the processing function now that you put it that way

Michael Stokley14:05:51

> is the idiomatic way to do that to basically have the map, have a function that transforms the map, and always invoke the map by calling the function instead? I think it's reasonable to transform data as it flows through a system to meet the needs of whatever component is making use of it - to basically have different data models for different regions of the code.

πŸ‘ 3
Endre Bakken Stovner13:05:21

Does anyone know what https://gist.github.com/endrebak/7d238ad32472d94edd93b9deba9c3406 might be due to? They happen when I run lein shadow watch app on a luminus app created with +shadow. The code works on another machine, but when I tgzipped and copied the entire repo to a different machine these errors popped up. An example error is [:failed-to-compare "16.8.6" "16.9.0" ...]

> npm ls
everclear@ /Users/endrebakkenstovner/code/everclear
β”œβ”€β”€ [email protected]
So I am pretty sure these errors are due to version mismatches, but I cannot see how to fix them. My package-lock.json:
1718-    "react-dom": {
1719:      "version": "16.8.6",
                     //^^^^^^^
1720:      "resolved": "",
Just remembered the project the errors come from is here btw: https://github.com/endrebak/everclear It is just a prototype under construction, so no point looking at the code.

Endre Bakken Stovner13:05:00

It seems like lein shadow watch app still works, but always complains about stale compilation output.

pinkfrog13:05:41

ns-resolve has an env param, what’s that for. (ns-resolve ns env sym)

Sebastian Ohlsson15:05:50

Hi guys rookie question here! Can someone give me an example of a next.jdbc transaction.

(defn main
  [budget]
(jdbc/with-transaction [tx ds]

                     
                       (for [b budget]
                         (sql/query tx ["
                        UPDATE hbi_bp.model_no_of_students
                        SET process_status_id = 'Locked'
                        WHERE calendar_id = ?
                        AND calendar_period_id = ?
                        AND organisation_pk = ?
                        AND version_id = ?
                        AND school_type_id = ?
                        AND program_id = ?
                        AND grade_year_id = ?"
                                        (:model_no_of_students/calendar_id b),
                                        (:model_no_of_students/calendar_period_id b),
                                        (:model_no_of_students/organisation_pk b),
                                        (:model_no_of_students/version_id b),
                                        (:model_no_of_students/school_type_id b),
                                        (:model_no_of_students/program_id b),
                                        (:model_no_of_students/grade_year_id b)]))
Im trying to go through an array of maps and update all those values that match without connecting to the db for each "for"-cycle. I can get it to work for the single case (a single row) but I cant go through an array. Any points would be awesome. Have a great evening.

ghadi15:05:06

for is a lazy sequence comprehension not a loop

ghadi15:05:30

need something to realize the sequence inside the with-transaction

πŸ‘€ 6
Sebastian Ohlsson15:05:26

I will look at it again with this. Thanks

Mark Wardle16:05:39

Hi all. Can I ask a style question please? I have a service that wraps two other services and provides a nice API. I have a deftype representing that unified facade and then in order to provide the implementation, I’ve added a protocol and implemented the methods for the type. But this feels wrong as I have no intention of providing multiple versions. So is it better to simply use the deftype as a way of holding some internal references and provide functions at a namespace level? I had thought it might be nice to provide Java interop in the future, but the reality is I’m more likely to write a proper Java wrapper and simply invoke functions anyway. TLDR; should I go through the contortions of defining a protocol simply to add implementation methods or is it fine to just use methods that take a handle of a (kinda opaque-ish) service implementation and forgo protocols completely?

Alex Miller (Clojure team)17:05:06

the cool thing about protocol functions is that from outside the namespace, you invoke them identically to how you'd invoke a non-protocol function

Alex Miller (Clojure team)17:05:08

so if you don't use a protocol, you can always start using one later in the implementation without breaking callers

Alex Miller (Clojure team)17:05:55

in general, it's usually better to make the API actual functions anyways (as then you have the opportunity to do custom logic there) and use a protocol for the SPI instead

πŸ‘ 3
Alex Miller (Clojure team)17:05:53

we follow that pattern in the spec impl and it's been very useful

πŸ‘ 3
didibus17:05:34

Doesn't sound like you need a deftype to me. Just use functions like so:

(defn make-service
  [other-service1 other-service2]
  {:other-service1 other-service1
   :other-service2 other-service2})

(defn some-api-for-it
  [service-map other-thing-you-might-need]
  ...)

(defn more-apis-for-it
  [service-map ...]
  ...)
And then a client does:
(def service-instance (service/make-service (FooService.) (BarService.)))
(service/some-api-for-it service-instance "12345")

Mark Wardle06:05:35

Thanks both. That’s really helpful. I will remove the protocol and make it as simple as possible for what I need now. I was getting itchy when i had to keep adding more methods to the protocol!

Ben Sless06:05:15

Another approach, especially when you need a single instance with no versions is to use reify. You can always reify into more protocols in the future.

πŸ‘ 2
Dave Suico16:05:21

Hi guys, I have to test a csv reader I made and I need to read a csv file that lives somewhere in the resource folder in my codebase. How can I traverse and locate my file? thanks!

Mark Wardle16:05:14

Hi. Have you seen http://clojure.java.io/resource https://clojuredocs.org/clojure.java.io/resource. As long as the other file is in your class path - you should be able to find it.

Dave Suico17:05:38

Hello there! Currently reading it now as you commented but I do not fully understand classpaths yet. Looking at the image, I think my sample file is separated from the classpath right?

Dave Suico17:05:31

altho I tried this and it works. Can I see a better call than this?

Mark Wardle17:05:22

It depends on your classpath. Which build tool are you using? You can include arbitrary directories in your classpath. The trouble with using an absolute filename is that it won’t work from a jar file - it works for your development environment only…. now I think some people use git and clj for production, but you’re better getting that path into the classpath, and using io/resource.

George Silva17:05:45

Hello friends! I'm pretty new to Clojure and I'm reading a few books and doing a few exercises. I have a few questions: 1. What are good resources in order to learn "design patterns" or more idiomatic code? 2. What is a good web "framework" (quoted because I do understand that frameworks are not really the clojure way) to get started? I tried several but they all seem a pretty complicated or have a pretty complicated boilerplate code (`lein new ...`). Any interesting resources for beginners that encompass API, database and maybe docker/docker-compose?

sb17:05:42

1) Clojure: The Essential Reference (meap) book > very good and helpful. Timothy Baldrigde Pivotshare video tuts (before this was on YouTube) similar good. Not exactly what you search, but I like both. Im not at laptop, maybe somebody can share better links to idiomatic code.

πŸ‘ 3
tvirolai09:05:42

β€’ The Clojure style guide (https://github.com/bbatsov/clojure-style-guide) is a good resource for idiomatic style. If you want to go deep into this, check out Zach Tellman's book Elements of Clojure (https://elementsofclojure.com/) β€’ A good way to learn to write idiomatic code is to get feedback on your code. Try checking out the Clojure track (in mentored mode) on http://exercism.com β€’ As to the framework thing, Luminus provides great configurable templates for getting your projects up and running: https://luminusweb.com/

Old account17:05:39

Hello! how can we describe the map that is embedded in this defn body

(defn ok []
  {:pre  [(keyword? 6)]
   :post [(number? %)]}
  4)
It is also evaluated and assertions run. What else can go into that map?

delaguardo17:05:00

It is called "condition-map" in defn form

βœ”οΈ 3
Alex Miller (Clojure team)17:05:34

you can also see info in (doc defn) (as "prepost-map")

βœ”οΈ 3
Alex Miller (Clojure team)17:05:41

those are the only things that can go in it

βœ”οΈ 3
Old account18:05:37

in (doc defn) documentation there is some attr-map? mentioned. I can not find a good explanation on what it is. What can it be?

Alex Miller (Clojure team)18:05:20

it's combined with the :meta

Alex Miller (Clojure team)18:05:20

it was included as an option at the end in case you have a large metadata map, you could put it after the main part of the function (using this is very unusual)

Alex Miller (Clojure team)18:05:28

most people don't even know that exists

πŸ˜€ 3
Benno LΓΆffler20:05:26

you may express pre- and post-conditions in Eiffel-style. Its basically assertions regarding the arguments and the result of the function. Example see http://blog.fogus.me/2009/12/21/clojures-pre-and-post/

David Pham17:05:46

Hello everyone :) I am trying to use http-kit behind a proxy and the final endpoint is in https and the proxy is in http. When using babashka.curl/post with Λ‹-x http://myprotoxy:my-port/%60 the request works, but it fails with org.http-kit.client. Same behavior with clj-http.client. Anyone would have a clue why that happens? [i am using Java 8]?

borkdude17:05:16

@neo2551 Did you enable the SNI client in httpkit?

borkdude17:05:46

and does it work with the (native) babashka httpkit client. rather than in Java 8?

David Pham17:05:51

Probably not.

borkdude17:05:56

(if so, then it's the SNI client problem)

David Pham17:05:57

Let me check with babashka.

David Pham17:05:04

Thanks for the help!

David Pham17:05:54

How would you configure it? Just require it?

David Pham17:05:13

Nope, it was not that xD

borkdude17:05:41

but does it work with bb then?

borkdude17:05:06

> but it fails with org.http-kit.client. Same behavior with clj-http.client oh, I see, then it's not http-kit specific

borkdude17:05:19

but still good to enable that SNI client

David Pham17:05:58

Thanks I will know for the future :)

David Pham17:05:15

I am just happy that you wrote curl because my colleague were showing the command by curl and I was a bit embarrassed to fail with httpkit. It also worked with python.requests (so I guess I could have used libpython-clj but in the worst case).

borkdude17:05:26

ooh it's a proxy

David Pham20:05:34

What if I want to make a proxy call for a few calls only?

borkdude20:05:48

Maybe you can unset the system properties using System/setProperty, not sure if that helps

David Pham07:05:22

I think the issue is I am using basic authentication method with user:password@proxy-url

borkdude07:05:46

Maybe read the Java docs about it, I would have to look it up as well :)

David Pham07:05:34

So now, I can make it working with clj-http but not http-kit haha

borkdude07:05:13

why is that?

David Pham10:05:58

I don't know, I have been trying to debug, but I can't log the calls from http-kit.

borkdude17:05:30

I missed that

borkdude17:05:37

you can set Java properties for this

borkdude17:05:21

e.g.:

java -Dhttp.proxyHost= -Dhttp.proxyPort=8080
see https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html This should also work with bb

πŸ‘ 2
borkdude17:05:43

with the clojure CLI it's -J-Dhttp.proxyHost= etc

πŸ‘ 2
Michael Stokley19:05:21

i have a question about the deps.edn approach to dependencies. our project has multiple dependencies, one of which happens to exclude another dependency that is otherwise required. when i include both, i start to see a lot of java.lang.RuntimeException: java.lang.NoClassDefFoundError: <<that excluded dependency>> runtime errors. my hypothesis here is that dependency A's exclusion is winning, globally, over dependency B's dependency. is my hypotheses likely? is this typical, or is the inter-dependency interference just idiosyncratic to my set up? is there a workaround?

Michael Stokley19:05:37

perhaps it's that my dependency that excludes is high up in the dependency tree

Michael Stokley19:05:56

and so the inter-dependency interference is working as intended?

ghadi19:05:03

why excluding something that appears mandatory?

phronmophobic19:05:28

If you want a specific version of a library, you should just make it a top level dependency with the version you want.

Michael Stokley19:05:12

it's two different third party libraries. in this case, djblue/portal , which excludes org.msgpack/msgpack, and datomic (which, i gather, requires it)

ghadi19:05:19

can you be more specific about the problem you're having, like a small reproduction case?

ghadi19:05:37

specifics matter and it's hard to guess

ghadi19:05:10

is djblue/portal specific bare within your deps.edn?

Michael Stokley19:05:13

i can try πŸ™‚. i depend on djblue/portal in ~/.clojure/deps.edn under :deps

ghadi19:05:29

with an exclusion or not?

Michael Stokley19:05:02

no exclusion, no. i'm peeking at https://github.com/djblue/portal/blob/master/deps.edn#L4 to come to the conclusion that org.msgpack/msgpack is excluded

seancorfield19:05:14

Bad idea to have a :deps alias in your user deps.edn file as it will shadow (hide) the :deps alias in your root deps.edn file. Maybe use :dev instead?

πŸ™Œ 2
ghadi19:05:59

ok -- and datomic is specified in your deps also without any exclusions, correct?

Michael Stokley19:05:21

datomic is specified in the deps of the project whose repl i'm invoking, with just one exclusion, but it's unrelated as far as i can tell (`com.amazonaws/aws-java-sdk-s3`)

ghadi19:05:37

can you paste the coordinate?

Michael Stokley19:05:46

`{:exclusions [com.amazonaws/aws-java-sdk-s3] :mvn/version "0.8.102"}`

ghadi19:05:55

including the datomic part

Michael Stokley19:05:56

{org.clojure/clojure       #:mvn{:version "1.10.0"}
 org.clojure/tools.logging #:mvn{:version "0.4.1"}
 com.datomic/client-cloud
 {:exclusions  [com.amazonaws/aws-java-sdk-s3]
  :mvn/version "0.8.102"}
 ch.qos.logback/logback-classic
 {:exclusions [org.slf4j/slf4j-api] :mvn/version "1.2.3"}
 javax.xml.bind/jaxb-api   #:mvn{:version "2.3.0"}
 resauce/resauce           #:mvn{:version "0.1.0"}
 org.clojure/tools.cli     #:mvn{:version "0.3.7"}}

Michael Stokley19:05:26

thank /you/ πŸ™‚

Michael Stokley19:05:01

> If you want a specific version of a library, you should just make it a top level dependency with the version you want. i don't want it. my dependency does. @U7RJTCH6J

ghadi19:05:04

what do you need to do to trigger an error, require a datomic namespace? or connect to a cluster?

Michael Stokley19:05:24

requiring works fine

ghadi19:05:16

what is that

ghadi19:05:27

conn/init-conn is not a datomic thing

ghadi19:05:41

a stacktrace would be helpful

Alex Miller (Clojure team)19:05:51

and a clj -Stree from the root project

ghadi19:05:56

*e when you get an error

phronmophobic19:05:06

there's no programmatic way to resolve conflicts between dependencies of dependencies. If you figure out which version might be compatible with both dependencies, then you can just add that as a top level dependency.

🀯 2
Michael Stokley19:05:46

sorry, my bad. here's the stacktrace:

Caused by: java.lang.RuntimeException: java.lang.NoClassDefFoundError: org/msgpack/MessagePack
	at com.cognitect.transit.TransitFactory.writer(TransitFactory.java:104)
	at cognitect.transit$writer.invokeStatic(transit.clj:157)
	at cognitect.transit$writer.invoke(transit.clj:139)
	at $marshal.invokeStatic(io.clj:48)
	at $marshal.invoke(io.clj:38)
	at $client_req__GT_http_req.invokeStatic(io.clj:76)
	at $client_req__GT_http_req.invoke(io.clj:73)
	at datomic.client.impl.shared.Client._async_op(shared.clj:398)
	at datomic.client.impl.shared.Client.list_databases(shared.clj:437)
	at datomic.client.impl.cloud.Client.list_databases(cloud.clj:153)
	at datomic.client.api.async$list_databases.invokeStatic(async.clj:135)
	at datomic.client.api.async$list_databases.invoke(async.clj:129)
	at datomic.client.api.sync.Client.list_databases(sync.clj:91)
	at datomic.client.api$list_databases.invokeStatic(api.clj:131)
	at datomic.client.api$list_databases.invoke(api.clj:125)
	at db.conn_v2$db_exists_QMARK_.invokeStatic(conn_v2.clj:9)
	at db.conn_v2$db_exists_QMARK_.invoke(conn_v2.clj:8)
	at db.conn_v2$init_conn.invokeStatic(conn_v2.clj:14)
	at db.conn_v2$init_conn.invoke(conn_v2.clj:11)
	at db.conn_v2$conn_cache$fn__28471.invoke(conn_v2.clj:36)
	at clojure.lang.AFn.applyToHelper(AFn.java:154)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$memoize$fn__6894.doInvoke(core.clj:6342)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at authentication.interceptors$inject_customer_db$fn__25378.invoke(interceptors.clj:161)
	at clojure.lang.AFn.applyToHelper(AFn.java:154)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$apply.invoke(core.clj:662)
	at io.pedestal.interceptor.helpers$before$fn__16276.invoke(helpers.clj:109)
	at io.pedestal.interceptor.chain$try_f.invokeStatic(chain.clj:54)

Alex Miller (Clojure team)19:05:08

I'd also be curious of your clj --version

Alex Miller (Clojure team)19:05:40

the only version of transit that's getting included is the one via djblue/portal, which is intentionally excluding msgpack

Michael Stokley19:05:09

~/tract-manager/projects/one-deps-repl(main) $ clj --version
Please install rlwrap for command editing or use "clojure" instead.
~/tract-manager/projects/one-deps-repl(main) $ clojure --version
Clojure CLI version 1.10.3.822

Alex Miller (Clojure team)19:05:50

I think the only way you can reconcile this is by including msgpack as a top-level dep, basically overriding djblue/portal's statement

Alex Miller (Clojure team)19:05:10

org.msgpack/msgpack {:mvn/version "0.6.12"}

ghadi19:05:16

including com.cognitect/transit-clj top-level seems to do the trick

Alex Miller (Clojure team)19:05:24

that would also work, yes

ghadi19:05:29

that's probably preferred

ghadi19:05:37

because it's not just msgpack that's omitted

ghadi19:05:00

clj -Stree -Sdeps '{:deps {djblue/portal {:mvn/version "0.11.1"} com.datomic/client-cloud {:mvn/version "0.8.102"} com.cognitect/transit-clj {:mvn/version "0.8.313"}}}'

Alex Miller (Clojure team)19:05:29

exclusions at the library level are likely to cause these kinds of issues

Michael Stokley19:05:47

i appreciate the help and the context, very much

djblue21:05:44

https://github.com/djblue/portal/releases/tag/0.11.2 will stop excluding transitive deps, didn't realize it would cause problems 😬

πŸ™Œ 2
seancorfield21:05:57

My experience with deps.edn and tools.deps.alpha is that :exclusions are almost never needed β€” unlike with Leiningen/Boot. This is the only exclusion in our entire codebase:

com.walmartlabs/lacinia {:mvn/version "0.35.0"
                           :exclusions [clojure-future-spec/clojure-future-spec]}
and that was needed because Lacinia includes the dependency so it works on pre-Spec versions of Clojure and would conflict with Clojure’s Spec if not excluded (as I recall β€” but we probably ought to verify that is still true).

seancorfield21:05:57

(yup, looks like we could remove that exclusion now)