This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-23
Channels
- # ai (1)
- # aleph (1)
- # announcements (7)
- # babashka (87)
- # beginners (34)
- # biff (9)
- # clerk (4)
- # clojars (37)
- # clojure (144)
- # clojure-art (12)
- # clojure-europe (13)
- # clojure-nl (1)
- # clojure-norway (4)
- # clojure-uk (2)
- # clr (5)
- # conjure (1)
- # data-science (1)
- # datahike (7)
- # datalevin (6)
- # datomic (13)
- # events (1)
- # fulcro (1)
- # graalvm (5)
- # gratitude (1)
- # honeysql (4)
- # hyperfiddle (122)
- # malli (26)
- # nbb (2)
- # off-topic (16)
- # portal (93)
- # practicalli (1)
- # re-frame (1)
- # reitit (15)
- # releases (3)
- # remote-jobs (1)
- # shadow-cljs (5)
- # tools-deps (6)
- # xtdb (4)
Is there something equivalent to the following built in?
(defn tee [x]
(println x)
(identity x))
Maybe a reader macro that would interleave the s-expressions with tee's as well?
#tea(count (take-while (partial > 3) (range 10)))
=> (tee (count (tee (takewhile (tee (partial > 3)) (tee (range 10))))))
if you use threading alot, https://github.com/TristeFigure/threading allows pp->
I'd argue that you have already written that function yourself in the OP. There's no need to add a separate library for it to just repeat those 3 lines.
You can also use doto
to make ->
threading easier without a helper:
(-> x
(doto println) ;; Or (doto tap>)
inc)
Seems like my function is equivalent to doto println
and my idea for a reader macro is hashp
Might write my own macro anyway, but good to have these
Also, if you need such a thing often or think that you might benefit from some more advanced features, seems like the FlowStorm debugger would be a good fit: https://jpmonettas.github.io/flow-storm-debugger/user_guide.html
Oh, doto
chokes on threadlast.
Don't really need it that often, cider enlighten-mode covers me most of the time for basic tracing
As discussed in another thread in the last day or two, println
is less than ideal for debugging because it hides useful type info (e.g. 2
and "2"
both print as 2
). You should generally find prn
is more helpful.
Similar to hashp, I use spyscope a lot: https://github.com/juxt-forks/spyscope
Hello! I know I can read a file with "slurp" function--> (slurp "blubber.txt") as giving file path as an argument. I need to read more than 10 files collected into a parent file. Is there any way to give just the parent file's path as argh and read all files into that? Or is it impossible? Should I read every file single by single?
What do you mean by "read files into a parent file"? Like cat a.txt b.text c.txt > all.txt
?
Or do you mean that there are many files in a directory and you need to read all of them?
Or do you mean that there are many files in a directory and you need to read all of them? ---> Yes in a directory
(->> ( "/path/to/directory")
(file-seq)
(filter (fn [^java.io.File file]
(not (.isDirectory file))))
(map slurp))
Wow thank you so much! I just wondering is it work into a directory of directories? i.e. (directory file file file child (directory file file file))
or
(->>
( "/Users/ivan/temp/foo/")
(file-seq )
(filter (fn [^java.io.File file]
(not (.isDirectory file))))
(mapv (fn [^java.io.File file]
{:name (.getName file)
:content (slurp file)})))
which is better because you'll have both files names and their contentI think many clojure devs need to read this https://justsimply.dev/
I so agree that “just” is very annoying to hear! “should” is another pet peeve of mine. Like “they should know that I want to do blah”
“should” invokes fantasy worlds that don’t exist. And if I had the ability to create a new fantasy world to live in I would create something cooler, where warlocks prowled etc
I find should is ok in some contexts, like "double clicking on the icon should start the game", leaving some room for things that can go wrong
I climb rocks as a hobby. I've noticed in the last few years that loads more people have been getting into the hobby I've been doing for 20 years, and the advice that I used to receive used the word "just" in that context. As in "just do a one arm pull-up" or "just reach the hold". The people that are now joining the sport are having trouble with that sort of advice. The word "just" seems to imply to them that it should be easy for them to do. Whereas I think that the people making the statement are not trying to suggest that doing a one arm pull-up is something that's easy for anyone, but they are using the word to imply that it's easy to explain, and there aren't any other steps you can do without going away and spending months or years training to do a one arm pull-up. It's often said with a knowing smile. So I've tried to stop using the word "just" when handing out beta to people at the wall. But this the first time I've seen someone talking about the same thing in a dev context, I think that I've kinda stopped using the word as much in general, but I'll bet I slip up more than I think I do.
Haha yes in that context it’s winking at how difficult it all is. “Just use your legs!”
Would be funny if some people have observed that effect more broadly, in a general setting. Oh wait. There is a book called Tyranny of Words by Stuart Chase, and an dead-science called General Semantics, that observed this and many other issues in trying to convey meaning etc.
I think anyone should read Science and Sanity, or any GS content. Most don't even know that things like "non-violent communication" came from non-GS scholars who have been exposed to the matter and inspired themselves to talk about one or two applications of GS
Does anyone here know of any analysis of the quantity or density of parentheses across programming languages? When I discuss Clojure with otherwise serious engineers, too often the conversation immediately gets hung up on this topic. I'm tired of trying vainly to assert that there is probably very little difference in practice. I'd like to just put the topic to bed with something more quantified.
Haha it is such a weird stereotype
Besides an extra pair around top level forms, it's the same amount as in method calls, just different order
Not really, since let bindings also introduce parentheses that other languages would not, but not to the extent that CL does. On the other hand, not using templates saves a lot of angle brackets. I'd love to have a simple chart with a few languages (including CL, C++, C, Python, Ruby, Scala, etc) with a normalized analysis of parens per line with quartiles marked out or something...
it’s similar to “squats are bad for your knees”, one of those reflex-ideas that I agree @U01D37REZHP we want a quick way around
Tbf stacking un-indented parens at the end of a form is pretty uniquely lisp and can look terrifying.
You have to learn to rely on structured editing, and trust just isn't something you can explain with numbers
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((fn rec [] rec)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
If you have to talk about structured editing, you've already lost, since you are now far off topic.
+100 to structured editing
not sure how accurate it is
Agree, however what you argue about is not up to you
That chart looks wrong
I think there's not an order of magnitude difference!
Idk "your concerns are dumb" seems a losing strat all the same. Having more motivating ideas/reasons seems better to me.
I think the key point to make is that all languages have syntax. Clojure leans heavily on annotating every expression boundary, but in exchange you get to drop .... everything else
You'd need to count { }
in non-Clojure code (as well as ( )
) to be comparable.
once you get used to Clojure's very regular syntax, it is hard not to see every other language as completely arcane
Alex is making good points
(to be clear, I'm not recommending saying "your concerns are dumb", just don't engage at all, or engage with something more productive, like hey expr regularity means you don't ever have to remember precedence rules)
and you then lead into treating code as data, macros, etc
No one is saying "your concerns are dumb", just preparing better for these inevitable asides to try to move rapidly beyond tehm.
if (x < 0) {
...
} else {
...
}
// three "parens"
(if (< x 0)
...
...)
;; two parens
Thanks @U050ECB92. I assumed that's what you meant ftr. I wanted to clarify is all.
Sean, yes, but such simple examples are easily dismissed as cherry picked
Agree with Alex up to mentioning Macros, which can open up a pandora's box for anyone experienced with CL and its very irregular macros.
But it isn't cherry picked. obj.method(arg);
vs (.method obj arg)
isn't cherry-picked either.
sometimes if someone has some Lisp awareness it's also useful to contrast Clojure with other Lisps in that others typically use parens for two purposes - "evaluate" and "group" whereas Clojure uses parens almost exclusively to mean "evaluate" and has literal syntax for different kinds of grouped data [] {} #{} OR completely elides some grouping entirely (pairs in binding forms)
you can mention macros by their purpose (constructing and altering programs themselves) without mentioning "macros"
and that's very aligned with structural editing as one of the pros of delimited expressions
this in Blub territory - when you use a language that can't construct itself it can be hard to imagine why you would want that
but it's an enormous power for expressivity
by which mean I mean a very high ratio of "what I can say" for "what I have to say it with"
Yes! It's been so long since I've started using Clojure it's hard to recall the things that I found most convincing to give it a real try...
I'm not sure there are good convincing arguments since lisp syntax is one of those things you need to experience in order to understand the benefits and that you just stop seeing the parenthesis after a while
Infix math vs prefix math is about the only place where there might be "noticeably more parentheses" and even that depends on whether the expression already needs parentheses to adjust for precedence. I think a lot of (non-Lisp) people just don't realize how much "syntax" their home language actually has... because they are used to it and just don't "see" it. In the same way that we are used to our parentheses and don't "see" them: https://corfield.org/articles/omg_parens.png
yes! but it's also helpful to show the reverse - what a Lisper sees in a non Lisp :)
When people read Java or C/C++ code, they just skip over a lot of { }
and ;
and so on...
Like MyParenFactory parenFactory = MyParenFactoryFactory.createParenFactory();
type stuff?
so going back to the OP, I think you shoot yourself in the foot by starting with "density of parentheses" - you're leaning into the thing others are afraid of. if you more generally talk about "complexity of syntax" or something, Lisps are objectively way simpler
just in having only expressions and not expressions AND statements alone, it's simpler
"everything is an expression" and "all expressions nest" is massive
the only thing I find a little harder with lisp syntax after so many years is adding comments to it, not sure if it's just me
there are like 10 different value types, and 4 kinds of literal collections, everything nests
This is all great material @U064X3EF3, thank you, it will be very useful.
https://clojure.org/about/rationale is good fodder too of course
This mode of objection usually comes in joke form, so a quick and (at least slightly) witty rebuttal is also very handy. So now thinking about directions for possible rebuttals....
• You're laughing but those parens have a lot more power than you might think!
• You prefer );\n};\n];\n);\n},\n};\n]});\n
?
• Assembly has no parens, if you want.
... ideas welcome 🙂
I like the Alex style of everything is an expression and expressions nest, then ask “can you think of a better way to do it?”
As an actual sincere question of course
Haha and if they have something good, great, let’s make a macro and use that instead!
Maybe a breezy "This is my favourite topic! Let's go deep!" is all that's needed.
> When I discuss Clojure with otherwise serious engineers, too often the conversation immediately gets hung up on this topic. Hearing this, I'm not sure if they're really "serious" engineers. Just avoid discussing such topics with them. Sometimes, you're the one right person even though everyone is laughing at you.
It's not a strict analysis and I largely agree with the advice above to sidestep the entire thing, but some kinds of people are convinced by this kind of article comparing Java and Clojure implementations: https://labs.ig.com/lines-of-code-matter > The only difference in readability is that the Java version has a lot more parentheses.
I'll gladly take a few additional parens over having to memorize operator precedence or the semantics of prefix and postfix versions of the same operator (e.g. pre vs post incrementing in C). Also note that most curly brace languages have the same level of noise in the form of commas and a pyramid of braces, parens, commas, semicolons, ..., consuming a third of your screen's vertical space
at the end of the day, people complaining about parens suffer from what some people call "baby duck syndrome"; it doesn't look like what they grew up with, so they immediately reject it its a similar phenomenon as people who say things like "windows 98 has a way better UI than modern windows"; most people saying this only think so because they grew up on win 9x
My friendly rebuttal is: “What your language lacks in parenthesis, it more than makes up for in commas, semicolons, and return statements.”
"Do you worry about all the spaces between words in a newspaper?" Mind you, they may never have seen a newspaper.
https://redmonk.com/dberkholz/2013/03/25/programming-languages-ranked-by-expressiveness/ might be interesting to you
Stu has done a couple of talks in the loosely coined "Simplicity/Power/Focus" series that touch on these ideas too • https://www.youtube.com/watch?v=cidchWg74Y4 • https://www.youtube.com/watch?v=KZ8u_sWT9Ls
Thanks for the extra resources @U064X3EF3 I must have seen these when they were new but it's time to rewatch! 🙂
He keeps threatening to do part 3 :)
Just needs to focus on it?
Just re-watched that Part 2 talk -- it's an interesting piece of history in some ways, because of how much has changed in the library and tools spaces in the last decade-ish!
Just use what I grew up with, BASIC or COBOL. COBOL is just 'English' and has no parens except in the data definitions of variables! Problem solved! :rolling_on_the_floor_laughing::rolling_on_the_floor_laughing::rolling_on_the_floor_laughing:
Here are two other answers from chatGPT.
I would not trust info like this from GPT even as a rough estimate, unfortunately. I just asked it exactly the same question, word for word, and it gave me totally different numbers.
ChatGPT is very confident about the completely wrong information it spews out:grin: I'm always surprised that people take its output at face value...
> He keeps threatening to do part 3 :) > Any ideas on how to make that happen? I'd love to watch a part 3. Start Halloway's talks have given me a more practical perspective on Clojure than Rich's talk, which I feel have been more about theory / concepts / ideas / principles. In contrast, Halloway's talks seem to dig into the weeds of what to do when you sit down at the computer to make some code.
Need a one-liner? Ask them if they are bothered by all the spaces between words in a newspaper? Mind you, they may never have read a newspaper.
I’ve got a time sensitive application (a music sequencer) that contains musical patterns in an atom. These patterns can be changed on the fly and they are read by the sequencing loop. The problem is that something in the function call that updates the atom seems to be causing hickups. A minimal version of the function is this one:
(defn p
[id pattern]
(swap! playback/patterns assoc id pattern)
id)
I’ve noticed that if I remove the id
that is returned, then the hickups go away. Any idea why this is happening?
Even when I turn this function into a macro the problem persists. (I wouldn’t rule out this to be a problem with cider itself or related to emacs, but not sure what could it be).What calls p
and what does it do with the return value? If you remove id
the return value of the function will be the return value of swap!
which will be pattern
I am calling it from the repl, it’s a live coding library I am working on, so nothing is done to the return value
do you store lazy sequences anywhere in the atom?
I think most of the stuff in there is realized, but there may be something in there. Mostly things come from instaparse.
See what happens if you call prn-str on the result of the call to swap and return id anyway
that would confirm either a race condition or that it's realizing lazy stuff that's causing it.
Seems like that was it
👍
Using prn-str
is a pretty common way to fully realize general clojure data, just be aware that it doesn't work if you've got java objects in the printed value, it doesn't realize closed-over values in functions, and it uses a bit of memory for the string that gets thrown away.
thanks
is that the best strategy, other than finding where the lazy seq(s) are reside?
It's the strategy I'd use if I need to get a quick hack in to make it work and get it out the door. To make something as good as possible though, I'd just document everywhere that things are being put into the patterns that sequences should be fully realized before being put into the playback if possible, and track down everywhere that's currently not and fixing them.
The alternative is to make it so that your playback code will go out of its way to make sure that the sequence is being realized in advance of the playback head reaching any part of it.
This would allow infinite sequences and lazy sequences to be used as a normal part of the library.
gotcha, thanks
I've used the following to force realization:
(defn doall* [s] (dorun (tree-seq seqable? seq s)) s)
Forcing realization is usually not a good idea and can be a sign that your design should be reconsidered, but there are some times where it makes sense.The data structure is a tree, though, perhaps with postwalk or something?
that’s what the tree-seq call is for.
otherwise, you could just call dorun directly
Awesome!
Is it an anti-pattern to turn Java collection types e.g. LinkedHashMap
into Clojure maps in order to only benefit from functions like update
? I can't control the incoming data structure otherwise I would be using Clojure maps to begin with.
But depends a lot what you’re doing with it later
> to only benefit from functions like update > Presumably you would also benefit from gaining immutability?
I'm reasonably comfortable with the Java Collections API so I could use the methods on the Map
interface, so truthfully it's just a question of (un)necessary allocations? But I suppose the JVM is like 10,000 steps ahead of me there.
Having Java collections in your code is fine. You only need to stress it somehow, e.g. with a type hint, a prefix or mentioning that in a docstring.
> Presumably you would also benefit from gaining immutability?
This can't be understated, however be wary of immutable wrappers around mutable objects
If the objects are mutable, it might be a good idea to leave things as-is, signaling that "here be dragons".
If update
was desirable, maybe I'd not only coerce to {}
but also walk
the whole structure and e.g. call bean
on the objects.
My 2c; there are other approaches/preferences.
Converting to a Clojure data structure is not an immutable wrapper. It's an immutable structure derived from data that happens to be mutable. So there should be no dragons, right?
If I wanted to preserve the mutable Java structure, and wanted update
functionally for just a single op, I would probably just suck it up and use Java. If I wanted to do even-just-several ops, I would probably throw together a function like update-in-place
(or maybe just update!
?) that mutably clones update
behavior.
Initially, I would mostly limit the scope to covering the data structure in question, generalizing only to other data structures that are accessed in the same way.
Regardless, unless you have good reason to think the cost of converting is too high, I like your original plan of "just make it Clojure, so I can work with it as Clojure."
I hope this triggers a library that does minimal allocation immutable wrappers around java data structures
Java has no-allocation immutable wrappers around Java data structures if you want that
> Converting to a Clojure data structure is not an immutable wrapper. It's an immutable structure derived from data that happens to be mutable.
IDK, for me a truly-immutable approach would be:
• fully recursive, not just converting the top data structure
• absent of any mutable objects as nodes, e.g. Dog
with getters and setters