Fork me on GitHub
#beginners
<
2024-01-17
>
jwind12:01:07

I have 2 questions: 1. When should I start reaching for macros as a clojurist? I'm still new-ish to the language and while I understand macros, I don't know when I should prefer them over writing functions that do similar things. 2. What are some good resources for learning how to do parallel programming in Clojure?

valerauko12:01:27

you'll know when you need macros. if you don't need them don't write them.

delaguardo12:01:03

1. When you hit limitations of clojure syntax/semantic. For example: you might want to conditionaly read but not evaluate some forms like in case implementation

delaguardo13:01:46

if you can use function β€” use function.

delaguardo13:01:09

2. "Clojure for the brave and true" has a nice chapter dedicated to concurrent programming with clojure. https://www.braveclojure.com/concurrency/

πŸ‘ 1
jwind13:01:22

great, thanks for the answers! So it's a good general rule then that I won't need macros unless I know I've hit a limitation.

jpmonettas13:01:04

1. If you are new to Lisps and macros, exploring them are fun and enlighten. Said that I don't think there is a point when you SHOULD start reaching for macros. Macros are very convenient in some situations for defining syntax specifically tailored to your problem, but this comes with a big trade-off, which will be readability for people not familiar with the new syntax (even you after some time) and it's semantics. So I think most people in the Clojure community agree to only use them when they can make a really big difference, but always prefer plain functions. 2. For concurrency and parallelism I think you can go pretty far with Clojure futures, atoms, refs, agents and reducers which you can read about in the Clojure reference https://clojure.org/reference. And since you can also work with all the concurrency utils the JDK provides, you can also apply whatever you learn on Java tutorials.

Nim Sadeh13:01:05

I went through the Brave Clojure concurrency chapter recently and now I can get quite a bit done with core.async. I highly recommend it.

kennytilton13:01:26

Forget macros until you see a duplicated pattern across multiple function usages that cannot be solved by extending the function's API. ie, Boilerplate required by an otherwise cool utility. In Common Lisp I wrote macros all the time to avoid funcall, but Clojure is a Lisp-1 so the only time I write a macro is for intense libraries that benefit ridiculously from lexical capture and reduced boilerplate.

jpmonettas13:01:44

I think macros are an interesting concept, useful beyond syntax extension, which is oc it's main use. You can also use macros over Clojure syntax, so a Clojure program that can rewrite another Clojure program at compile time, which can be useful for instrumenting for debugging or profiling. Or in some extreme cases like core.async go block a compiler can be written in them. This aren't things you will be reaching for every day, but I think it is something worth visiting when coming to a Lisp, since it is a pretty unique and fun thing.

mloughlin17:01:57

Coming from Common Lisp, the more Clojure I wrote the fewer situations I found appropriate for macros. Data first is such a strong paradigm

πŸ’― 1
Vincent20:01:36

Macro is code that can generate code

Vincent20:01:02

if you have some specific purpose where it makes sense to have some insane shorthand that can generate other clojure code for you, that can later be compiled/run/interp'd... then that's a good use-case

Vincent21:01:55

one such case is tech-ninja tooling, you can make stuff that modifies other functions in your extant (currently existing) libraries and have it output more info, or save info to a file, although maybe this is also possible with alter-var-root macros are often used for making custom libraries where you're making a new "defn" or a "def" for the specific use-case. But really, macros let you pre-process things and end up with code as the output. If you find yourself repeating an idiom 50-100 times, then maybe consider a macro. Something like that.

Vincent21:01:51

in a decade of leveraging clojure i have had to write a macro never, and only recently when i was dealing with clojurescript did i think it would be remotely useful in not repeating myself. so that's my anecdote for you. others are actually wizards though and might use them more frequently.

delaguardo21:01:59

better to rethink and refactor what was causing those problems then introduce a macro πŸ™‚

πŸ’‘ 1
πŸ‘ 1
jpmonettas21:01:34

> others are actually wizards though and might use them more frequently. please macro wizards don't make it harder for the rest of us πŸ™

πŸ‘ 1
jwind12:01:45

wow thanks everyone for all the responses! I think I've gotten the message, avoid macros

πŸ˜‚ 1
kennytilton13:01:29

That might be the wrong message, and I confess to contributing to it. But can you now state when macros would be the perfect solution? It is not a beginner/expert thing -- your first project might require them! It just depends on the code you write first, and no one knows what that is. But here is a problem: if we do not have at least some familiarity with what can be expressed only with macros, we may not recognize when to use them. Now consider that a true Lisper will ask first, when told about some new form of Lisp, "Does it have homoiconic macros?" and one might conclude, geez, what are these creatures? Hell, John McCarthy's deepest insight may have been that code should be data and data should be code. Macros let us take some code, process it like any other data, and emit new code. Again, do we not want to know what they are, through actual experience? :thinking_face:

πŸ‘ 1
Jim Newton15:01:15

I'm trying to read the doc for transduce and I noticed that the parameter contains form but the doctoring doesn't mention anything about how xform is used. https://clojuredocs.org/clojure.core/transduce I think I'm beginning to understand what the 2nd argument does, but the first argument is not clear

ghadi15:01:57

xform transforms the reducing function

ghadi15:01:16

xform is the transducer you want to pass

1
Jim Newton15:01:50

and is there a definition of transducer somewhere?

Jim Newton15:01:08

I see several examples, but I'm looking for a definition

Jim Newton15:01:00

seems like one way to get a transducer is to compose existing transducers

Jim Newton15:01:53

looks like, perhaps, a transducer is a function which takes a reducing function and returns a reducing function.

Bob B15:01:57

<https://clojure.org/reference/transducers> might be a helpful introduction.. it includes a definition in the terminology section: > A transducer (sometimes referred to as xform or xf) is a transformation from one reducing function to another

βž• 2
ghadi15:01:19

yes, please prefer reference docs and not http://clojuredocs.org

ghadi15:01:50

clojuredocs is example based and will not get you the rigor you're looking for

Noah Bogart15:01:49

clojuredocs is great for looking up docstrings/arglists and some simple examples, but you gotta go to the reference pages for anything deeper

henrik16:01:12

(transduce (comp (map inc) (filter even?)) + 0 (range 10))

Jim Newton12:01:17

yes Henrik, there are examples of how to make a transducer given other transducers. but I was lacking the definition of what a transducer actually is

henrik12:01:36

Sorry, I thought you meant in the spirit of β€œshow me the shape of one”, not in the academic sense

henrik13:01:06

Rich Hickey has some videos digging into the nature of transducers, and they are (as usual) quite good. E.g. https://youtube.com/watch?v=4KqUvG8HPYo

Jim Newton12:01:23

by "what it is" I mean enough about it so I could implement it in another language. If I could implement it in Common Lisp, or python, or scala, or even from scratch in Clojure, I'd understand it better

Jim Newton12:01:41

this would separate syntax from semantics. part of what makes transducers interesting in Clojure is the conciseness of the syntax. you might loose that in another implementation in a different language. and understanding why the syntax is difficult leads to better understanding the problem in general (in my opinion anyway)

Omer ZAK23:01:57

I have some code which runs just fine in clj repl, but fails in lein repl.

$ clj
(require '[scaryui.core])
(scaryui.core/-main)
Runs fine. However:
$ lein repl
(require '[scaryui.core])
(scaryui.core/-main)
aborts with
java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "x" is null
The codebase in question is complicated, is being actively developed, and is not fully debugged or tested. Looks like some difference in some aspect of the running environment (versions, classpath or whatever) in lein repl vs. clj. Please advise how and where to look for differences in the running environments in lein repl vs. clj. I expect to be able to fix the problem on my own once I find the difference. Thanks!

phill23:01:00

Perhaps a difference in system properties? You can compare System/getProperties . You can also try (for a 3rd data point) lein trampoline repl which carries less baggage from Leiningen's own runtime stuff.

πŸ™ 1
Bob B23:01:14

you can also try lein classpath and clojure -Spath to get the classpaths for diffing (deps and lein have different dependency resolution strategies)

πŸ™ 1
hiredman23:01:51

why would they be the same?

Omer ZAK23:01:25

@U0NCTKEV8 My goal is to find the differences.

hiredman23:01:54

well a lein project is defined in a project.clj file

hiredman23:01:04

clj reads deps informaion from deps.edn

hiredman23:01:12

the two are entirely distinct things

hiredman23:01:55

the library clj is built on tools-deps cannot consume a project.clj, and while there are plugins for lein that can make it try and use a deps.edn file for dependencies, my experience is that they only work at the most trivial surface level (maybe that has changed it has been a while)

hiredman23:01:17

so I would be more surprised if lein repl and clj got you something that behaved the same

hiredman23:01:13

there are some differences, I think how they select which transitive dep to use if there is a version conflict, where I don't think there is a way to reconcile them (I think lein picks the highest version number like maven does, but maybe tools-deps picks the shortest path? not sure)

Alex Miller (Clojure team)23:01:31

(pst *e) would tell you where the error was coming from

Alex Miller (Clojure team)23:01:01

Work the problem you have

hiredman23:01:16

looking at your messages in #C033FTUBWH5 it looks like you glueing clj and lein together using a git-deps lein plugin and then just adding the place where the git-deps lein plugin stores the checkout to the paths for clj

hiredman23:01:14

it looks like are also including the path for the git dep in the list of paths for lein, which doesn't seem correct apparently that is how it is done

Omer ZAK23:01:22

@U0HG4EHMH, Turns out that lein trampoline repl does not work for me, with the current project.clj due to:

ClassCastException class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')
And at the moment I am too tired to find the offending dependency.

Omer ZAK00:01:46

I also compared the output of (System/getProperties) in both lein and clj environments. The major difference was in the classpath, very little surprise. But there were also some other differences. My conclusion is that sometime, someone will have to debug HumbleUI (the software with which I played) and get it to work also under Leiningen. I might accept the challenge - it would be a good learning opportunity. Please encourage me by telling me how important is it for you to have Leiningen support for everything in Clojure. πŸ™‚

valerauko13:01:16

I use lein for everything except clojuredart, so yeah it's important that stuff Just Works

πŸ‘ 1
1
phill01:01:31

@U06BDSLBVC6 if the question is essentially "should a library be released to Clojars for use by Leiningen and Clojure-Maven-Plugin users?" then you have my vote for Yes... a git-deps plugin for Leiningen won't help a Clojure-Maven-Plugin user.