Fork me on GitHub
#beginners
<
2022-05-01
>
Jon Olick02:05:31

Working on defmacro, and what is the ~symbol shorthand doing there? evaluating?

(defmacro unless [pred a b]
  `(if (not ~pred) ~a ~b))

phronmophobic02:05:42

The short answer is that ~ only wraps a form in (clojure.core/unquote <the form>)

> (prn '~foo)
(clojure.core/unquote foo)

Jon Olick02:05:16

so ~ is unquote?

👍 1
phronmophobic02:05:36

Yes, but it's used in a special way by the backtick operator

phronmophobic02:05:35

> (prn (let [x 42]
       `(+ 1 (clojure.core/unquote x))))
(clojure.core/+ 1 42)

Jon Olick02:05:36

still pretty confused

phronmophobic02:05:11

which is the same as:

> (prn (let [x 42]
       `(+ 1 ~x)))
(clojure.core/+ 1 42)

phronmophobic02:05:07

I'm sure most clojure tutorials on macros will explain it better, but the gist is that macros let you programmatically write code. One of the easiest ways to write code programmatically, is just write it normally, but with a few substitions. That's what the backtick and unquote help you do.

Jon Olick02:05:10

user=&gt; `~five _; within a syntax quoted block, ~ will turn evaluation back on just for the next form_ 5

Jon Olick02:05:14

that made it click

phronmophobic02:05:25

Yep, not sure why I tried to make my own explanation after providing links to better explanations 😆 . That is a better way to put it.

Jon Olick02:05:26

don’t be! you were super helpful!! Thanks!!

😅 1
noisesmith14:05:33

I tend to think of ~ as splice - ` creates a list literal, ~ splices into that list

Jon Olick02:05:17

and the `() shorthand

🧵 1
Jon Olick02:05:39

thats not a quote, which would be ’(), thats a back-tick

🧵 2
seancorfield03:05:38

The main difference there is that quote (which is what ' produces) gives you a completely literal form whereas the backtick used in macros will resolve symbols in context:

user=> `map
clojure.core/map
user=> 'map
map

seancorfield03:05:15

Which is more obvious with something like this:

user=> '(map inc (list 1 2 3))
(map inc (list 1 2 3))
user=> `(map inc (list 1 2 3))
(clojure.core/map clojure.core/inc (clojure.core/list 1 2 3))

Jon Olick02:05:39

so, ` is basically, pre-resolve these symbols instead of at eval time

Jon Olick02:05:31

Is there any reason to write a macro a’la defmacro and not put the body inside a backtick?

seancorfield02:05:31

Yes, there are sometimes things you want to do entirely at "compile" time instead of just producing code to execute at runtime. But it's fairly rare. You will often see macros that have some initial computation for "compile" time and then produce code from that initial data.

Jon Olick02:05:12

are there anonymous macros? like (fn) ?

Jon Olick02:05:03

also I assume that you can do macros with variable numbers of arguments like fn’s too

seancorfield05:05:54

Sorry, got distracted by life. Yes, macros can be variadic @U036UA9LZSQ but I'm not sure what you mean by "anonymous macros"?

seancorfield05:05:29

I mean, yes, a macro is "just" a function with metadata that says it's a macro but I don't know how you would use such a thing?

user=> (map ^:macro (fn [s] `(list ~s ~s)) '(:a (1 2) "three"))
((clojure.core/list :a :a) (clojure.core/list (1 2) (1 2)) (clojure.core/list "three" "three"))

caumond09:05:31

Hello, I've seen many times clojure sources which separates clj, cljc and cljs in src/clj src/cljc and src/cljs dirs. So I did the same. But now, I feel this a little bit cumbersome and have the feeling I can put all in the same dir. And shadow tells it is not recommended to do it: https://shadow-cljs.github.io/docs/UsersGuide.html#source-paths What are the last community thoughts on that subject?

Daniel Craig14:05:50

I've always just followed this convention without challenging it, I wonder what would happen if you tried what you're suggesting. Maybe errors?

kennytilton17:05:05

I wonder what "It is useful if you want to keep your tests separate for example." means. :thinking_face:

Jim Strieter10:05:47

What is an idiomatic way of writing an event loop in Clojure?

emccue14:05:39

Well, kinda depends what behavior you want out of the loop

emccue14:05:06

Can you give an example of what you think the idiomatic way to write an event loop is in another language you know?

xbrln14:05:16

Hello all, am trying to pass a function from integrant config to integrant init-key defmethod. My use case is that I have a publisher/subscriber listener setup with redis and integrant, and I want to pass the subscriber function from integrant config file to integrant init-key. This is my config file

{:db/redis		      {:redis-url ""}
 :listener/favourites {:conn       #ig/ref :db/redis
                       :channel    "store-in-redis"
                       :subscriber service.subscribers/store-in-redis}}
This is the integrant init-key defmethod for the listener. It is not able to call the function that I pass from config, but when I directly call the function inside the init-key, then it is working.
(defmethod ig/init-key :listener/favourites
  [_ {:keys [:conn :channel :subscriber]}]
  (car/with-new-pubsub-listener
    (:spec conn)
    {channel (fn [msg] (subscriber msg conn))}
    (car/subscribe channel)))
Could someone tell me what is the correct way to pass the function from integrant config to the init-key defmethod ? Thank you 🙂

Serafeim Papastefanos17:05:15

I've seen that we need the alter-var-root function to change the value of a variable? can't we use def instead ?

seancorfield17:05:46

Can you provide a bit more context? Those are not equivalent functions -- or use cases.

Serafeim Papastefanos17:05:44

it is explained nicely 🙂

seancorfield17:05:32

Yeah, those answers cover most of the cases.

seancorfield17:05:40

(and all of those cases should be rare in idiomatic, real-world Clojure -- there are certain times when alter-var-root is what you want but almost no times when using def (or defn) multiple times is appropriate)

Serafeim Papastefanos17:05:07

i learned about alter-var-root from your answer about how to use component 🙂

seancorfield17:05:46

If you're using Component, you may end up using alter-var-root to set/modify a global reference to the (running) app for the purpose of referencing it from a REPL -- which we do at work for apps where we run Socket REPLs and the -main starts the system up and also sticks it in a global so that when we connect a REPL to the running production process, we can (require '[ :refer [sys]]) to get hold of that instance.

Serafeim Papastefanos17:05:24

it is a really niche function

seancorfield17:05:50

Yup. But very useful since it is atomic / thread-safe and modifies the value by application of a function.

👍 1
seancorfield17:05:21

In 130K lines of Clojure at work, we have 83 calls to alter-var-root in 31 files and they are nearly all test fixtures -- and the rest are nearly all setting up global references to Component instances.

seancorfield17:05:30

(I just grepped out of curiosity)

Serafeim Papastefanos17:05:03

it has very specific usages

1
Baye19:05:47

Hi. Curious beginner/beginner here. 1. For those who swear by Emacs, are there things in Emacs for Clojure development you like better than Cursive? 2. Can you as easily develop in Emacs as you can in Cursive given Cursive has first class support for refactoring code / debugging (in general use the heavy IDE tools of Cursive)… 3. For those who avoid Emacs because of “breakages”, aren’t there popular dot files (by known Clojure programmers say Eric Normand’s) that one copy and update regularly? 4. Note I do not mean to fan any flames of Emacs vs..other fights ..Just asking for informed opinions. Thanks

seancorfield19:05:04

I used Emacs for about 20 years, on and off, and finally gave it up and moved to Atom (initially) and then to VS Code for Clojure work. I'm in #3 for that and I tried pretty much every curated Clojure dot-file setup as well as building several of my own configs.

seancorfield19:05:30

I don't like IntelliJ -- I have tried it multiple times over the years. JetBrains even gave me free licenses at several points, back when I wrote for magazines so I could review it but I just never liked it (and I never wrote a bad review since they tried so hard to get me to like it).

seancorfield19:05:17

I've been doing Clojure for over a decade at this point and I don't find much need for "refactoring" assistance -- I needed it for Java, back in the day, but Clojure is so much simpler and there's so much less boilerplate code around it.

seancorfield19:05:30

My general advice for beginners is: use whatever editor/IDE you already know, as long as it has Clojure support. Don't try to learn a new editor when you're also learning Clojure (especially if that means learning Emacs from scratch!)

5
seancorfield19:05:01

When I started with Clojure, I actually used TextMate for months 🙂

seancorfield19:05:26

Over the years, I switched back and forth between Emacs, Eclipse (CCW), LightTable, Nightcode... before settling into Atom (protorepl, then Chlorine) and finally VS Code (Clover from the Chlorine author, and then I added Calva as well).

seancorfield19:05:30

And, re: #4 @U01BDCAV7NZ these are very reasonable Qs to ask when you're getting started and no one is going to be offended.

Baye20:05:37

Greatly appreciate your thoughtful response @U04V70XH6. Thanks!

Baye21:05:57

Point well taken on Clojure focus...but it's enjoyable to use tools such as maggit and org mode. I already have some basic emacs skills such as setting up things, etc. The premise of my question was, if one could find a stable, famous dot file one can emulate then one would benefit from Clojure emacs experience: however, I was curious to know what Emacs could do that other editors are lack or not great at.

seancorfield21:05:09

I missed magit initially when I switched away from Emacs but git support in VS Code is pretty good (and getting better all the time -- and the Atlassian plugin is something I live in, all day, at work). I never got into org-mode but for some people that is the deal-breaker.

seancorfield21:05:49

Prelude is probably the best starting point for Clojure for Emacs -- maintained by the same person behind CIDER and nREPL.

practicalli-johnny13:05:09

1) I use Spacemacs and I can simply add layers that provide the extra functionality I need and it just works. It has a consistent set of key bindings across all languages (and other text modes), so very easy to switch. The key bindings follow mnemonic design so are easy to type and remember (I did struggle with the Emacs chorded key bindings and found this limited my use of Cider) Vim-style editing has become essential to me, without it I feel like I am wading through treacle. Emacs allows me to control everything from the keyboard Emacs is very fast, especially now it has native compilation and greatly improved JSON support (still struggles a with crazy long lines, but there are tools to manage that) In contrast I found IntelliJ complex to use when developing in Java, Scala and still confusing when trying Cursive. Although the docs give help in some areas, it seems you still need to define a bunch of key bindings for essential things like structural editing (it has been a while, so maybe this has improved) Also IntelliJ takes up significantly more memory than Emacs (again this may have improved) I am sure if I invested more time I could resolve these issues, however, cursive is still a closed and commercial product and I prefer using Open Source tools where ever possible.

👍 1
practicalli-johnny13:05:28

2) I don't think I got as far as cursive refactoring, as I struggled to just run a REPL without having to configure cursive to do something. Cider has some core refactor tools in Clojure+mode and there is also clj-refactor, although I have dropped thar in favour of Clojure-lsp (which uses clj-kondo). Clojure-lsp is fast and efficient (and open source)

👍 1
practicalli-johnny13:05:08

3) Emacs has several community configurations that provide an excellent Clojure (and many other languages) support Spacemacs Doom Prelude Corgi (LambdaIsland team) These project are all very well maintained and easy to get help on the rare occasion something changed (which is almost always down to a change in an Emacs package)

👍 1
Baye16:05:20

Thanks @U05254DQM; very helpful.

practicalli-johnny17:05:37

I also using Neovim and Conjure (configuration with fennel), although there are a lot of packages and config to figure out to replicate all the functions I currently use. Clojure-lsp makes it a lot easier to have a rich Clojure editing experience regardless of editor used I also appreciate VSCode & Calva (especially used with VSpaceCode for vim-style editing). To me, an editor should have a good REPL interaction experience, Vim-style key bindings, a magit style git client, LSP support and simple to use (and remember) key bindings

👍 1
Baye19:05:38

I really like this font and theme (especially the font) and the whole emacs setup from Eric Normand's course but cannot find his dot file. Any idea what the font is?

NickName20:05:25

I'm trying to do pattern matching with tests against regular expressions. I'm using core.match and following this guide—https://github.com/clojure/core.match/wiki/Extending-match-for-new-Patterns. However, I'm stuck because... They redefine multimethod pattern-compare there, but there is no such multimethod in clojure.core.match namespace. Search in the source repo shows that this name is not encountered anywhere. Any ideas what and where might be going wrong?

Bob B21:05:06

I'm kind of guessing/inferring with the hope that either this will lead you toward clarity or someone else will come along and fill in the gaps... That wiki page is from 2012, and seems to be out of date - there's a (I think) newer occurrence of similar docs at <https://github.com/clojure/core.match/wiki/Advanced-usage#extending-pattern-matching>, which says it's out of date and to check the regex example in the tests. I think a place to start might be <https://github.com/clojure/core.match/blob/master/src/main/clojure/clojure/core/match/regex.clj> - this has an implementation for regexes. My guess is that pattern-compare has sort of evolved into groupable? , presumably because the use of comparison is primarily to decide equality

👏 1
NickName22:05:47

Wow that's exactly what I was looking for. Thanks sir! Regarding pattern-compare, it does indeed seem that it was replaced by groupable?. Seems to be a more semantic naming which better reflects the purpose of the method

NickName23:05:04

I thought I'd send PR with regards to the page with outdated guide (the one I posted) and accidentally deleted it. (The delete button on the edit page actually deletes it although I do not have any special rights there.) Luckily, this wiki is just a git repo and it could be easily restored so I wrote to maintainer and if needed he can restore it. Sorry if I caused any inconveniences in the meantime.

Serafeim Papastefanos20:05:41

let's suppose i package an app in an uberjar and run it in production; is there a way i get a repl on that running app ?

seancorfield20:05:31

Yes, we do this in production. Look at the JVM options that tell Clojure to start a Socket REPL.

seancorfield20:05:03

Then you don't need any extra dependencies and you can connect to the running process with telnet or something like Clover (VS Code).

seancorfield20:05:36

If you really want nREPL, then you need nrepl as a dependency when you build your uberjar and you need explicit code inside your app to start an nREPL server.

Serafeim Papastefanos20:05:57

ah so it's just a java option? thank you ! does this have any kind of security ?

seancorfield20:05:42

Well, the REPL is complete Clojure execution so you can do anything...

seancorfield20:05:55

We have it set up so the server doesn't expose the port and it's all behind a CDN and a load balancer, so we have to VPN and set up an SSH tunnel to be able to connect. But even so, you can get your system easily enough from a REPL 🙂

seancorfield20:05:43

For us, that is slightly mitigated further by AOT-compiling code for the uberjar with direct linking so you're less likely to accidentally break code -- we really use those production REPLs for inspection and debugging only, often just for examining data in context.

👍 1
noisesmith14:05:46

the biggest barrier I found with this method was that people didn't know how to use a naked repl - they only had experience with nrepl as embedded in their IDE

noisesmith14:05:43

definitely don't put nrepl in prod (someone uses a familier keybinding and boom)

Serafeim Papastefanos17:05:00

Can't we connect to that naked repl with calva or clover?

seancorfield17:05:06

Clover (VS Code) will connect to a plain Socket REPL, yes.

👍 1
seancorfield17:05:02

@U051SS2EU If they don't know how to use a "naked repl", they probably shouldn't be allowed to connect to production anyway 🙂

Nundrum22:05:56

Is there some good doco for working with vectors and other structures that retain order? It seems there is a lot hidden knowledge when dealing with vectors. But I can't find anything that puts all that together in a useful way.

seancorfield22:05:36

I'm curious what sort of thing you're asking for and what's missing? It sounds like you have some specific Qs that aren't answered in the docs you're finding...?

phronmophobic23:05:57

I find that the "missing docs" are often just using other data structures. Here are some common alternatives: • https://github.com/clojure/core.rrb-vectorhttps://github.com/clj-commons/ordered For more options. check out the "Data Structures" section on https://www.clojure-toolbox.com/

Nundrum00:05:22

What's missing is my understanding of which functions work on vectors and retain order, as well as which have different performance characteristics. I realize I've picked up bits and pieces of knowledge about that, but don't have a complete picture.

seancorfield00:05:42

I guess I'm not sure what's missing from https://clojure.org/reference/sequences and https://clojure.org/reference/data_structures (see collections and vectors) in that regard... vectors are sequential and indexed by definition. The docstrings of functions that have performance guarantees mention those in all cases (if it isn't mentioned, it isn't guaranteed)...

seancorfield00:05:32

Can you give a specific example of something that you wouldn't know how to reason about given your "bits and pieces of knowledge"? That might help me understand what to suggest...

seancorfield01:05:53

I expect there's more narrative that could usefully be added to those pages if it would help (but folks are going to need to suggest verbiage or at least highlight where things are confusing/missing). http://ask.clojure.org is good place to start a discussion around improvements to the clojure-site repo

Nundrum01:05:14

Is there somewhere that information about those performance guarantees are collected? I think my confusion started when learning that peek works differently for lists and vectors. Which got me wondering why it is different.

seancorfield01:05:33

It's listed here under Vectors: https://clojure.org/reference/data_structures#Vectors along with several other vector producing/changing functions.

seancorfield01:05:39

I think what's not explicit there is probably the implication of log32(n) which produces amortized O(1) access for a lot of things?

seancorfield01:05:02

(the section above that talks about lists -- and those two sections probably could be clearer about lists being optimized for access/addition at the front and vectors being optimized for random access and for addition/access at the end?)

seancorfield02:05:22

That page also doesn't mention "optimized for" and hardly mentions performance guarantees explicitly -- which it could be expanded to include. Would that help provide more of a big picture there @U02UHTG2YH5?

seancorfield02:05:18

Perhaps this old issue could be expanded on as the root of more explicit description? https://github.com/clojure/clojure-site/issues/223

seancorfield02:05:23

Would you mind if I copied this thread into that issue as a guide for what could be added/expanded on http://clojure.org @U02UHTG2YH5?

Nundrum12:05:51

Yeah, bringing that together in one place would be helpful! Maybe I'm not really missing much, but it's hard to tell when pulling in docs from many sources. The link to Stephan Tilkov's blog in issue 223 is good. And yes please, copy this in.

Jon Olick02:05:31

Is there any reason to write a macro a’la defmacro and not put the body inside a backtick?