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)