This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-01-17
Channels
- # aleph (57)
- # announcements (2)
- # babashka (11)
- # beginners (55)
- # calva (18)
- # cider (37)
- # clj-kondo (24)
- # clojure (18)
- # clojure-europe (80)
- # clojure-nl (2)
- # clojure-norway (22)
- # clojure-uk (7)
- # clojured (1)
- # clojurescript (24)
- # data-science (9)
- # datomic (45)
- # events (1)
- # gratitude (4)
- # humbleui (30)
- # hyperfiddle (7)
- # introduce-yourself (2)
- # malli (3)
- # missionary (12)
- # music (1)
- # off-topic (33)
- # re-frame (3)
- # reagent (11)
- # ring (24)
- # shadow-cljs (13)
- # sql (3)
- # tools-deps (4)
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?
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
if you can use function β use function.
2. "Clojure for the brave and true" has a nice chapter dedicated to concurrent programming with clojure. https://www.braveclojure.com/concurrency/
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.
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.
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.
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.
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.
Coming from Common Lisp, the more Clojure I wrote the fewer situations I found appropriate for macros. Data first is such a strong paradigm
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
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.
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.
better to rethink and refactor what was causing those problems then introduce a macro π
> others are actually wizards though and might use them more frequently. please macro wizards don't make it harder for the rest of us π
wow thanks everyone for all the responses! I think I've gotten the message, avoid macros
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:
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
and is there a definition of transducer somewhere?
I see several examples, but I'm looking for a definition
seems like one way to get a transducer is to compose existing transducers
looks like, perhaps, a transducer is a function which takes a reducing function and returns a reducing function.
<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
yes, please prefer reference docs and not http://clojuredocs.org
clojuredocs is great for looking up docstrings/arglists and some simple examples, but you gotta go to the reference pages for anything deeper
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
Sorry, I thought you meant in the spirit of βshow me the shape of oneβ, not in the academic sense
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
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
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)
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!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.
you can also try lein classpath
and clojure -Spath
to get the classpaths for diffing (deps and lein have different dependency resolution strategies)
@U0NCTKEV8 My goal is to find the differences.
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)
so I would be more surprised if lein repl
and clj
got you something that behaved the same
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)
(pst *e) would tell you where the error was coming from
Work the problem you have
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
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
@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.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. π
I use lein for everything except clojuredart, so yeah it's important that stuff Just Works
@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.