Fork me on GitHub
#clojure
<
2023-05-23
>
port1908:05:25

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))))))

p-himik08:05:52

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)

port1908:05:38

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

p-himik08:05:20

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

port1908:05:55

Oh, doto chokes on threadlast. Don't really need it that often, cider enlighten-mode covers me most of the time for basic tracing

skylize14:05:15

> Oh, doto chokes on threadlast. >

(->> foo
 (#(doto % prn))
 bar)

skylize14:05:47

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.

Ernesto Garcia07:05:50

Similar to hashp, I use spyscope a lot: https://github.com/juxt-forks/spyscope

Can11:05:51

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?

p-himik11:05:50

What do you mean by "read files into a parent file"? Like cat a.txt b.text c.txt > all.txt?

p-himik11:05:15

Or do you mean that there are many files in a directory and you need to read all of them?

Can11:05:05

That's an example, some files into a parent file

Can11:05:58

Or do you mean that there are many files in a directory and you need to read all of them? ---> Yes in a directory

igrishaev11:05:32

(->> ( "/path/to/directory")
       (file-seq)
       (filter (fn [^java.io.File file]
                (not (.isDirectory file))))
      (map slurp))

igrishaev11:05:00

and you'll get a seq of strings read from these files

Can11:05:34

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))

igrishaev11:05:41

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 content

igrishaev11:05:11

nested dirs are also supported by file-seq

igrishaev11:05:32

you only need to filter them out with (not (.isDirectory file))

2
Can11:05:54

thank you so much for helping 🙂

Can11:05:12

such a lovely and helpful community ❤️

valerauko14:05:00

I think many clojure devs need to read this https://justsimply.dev/

Sam Ritchie15:05:51

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”

Sam Ritchie15:05:40

“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

p-himik16:05:46

Unless it's SHOULD from some RFC. :)

❤️ 2
jpmonettas16:05:47

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

👍 2
Ed16:05:01

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.

Sam Ritchie16:05:18

Haha yes in that context it’s winking at how difficult it all is. “Just use your legs!”

BuddhiLW17:05:19

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.

BuddhiLW17:05:20

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

Darrick Wiebe15:05:23

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.

💯 1
Sam Ritchie15:05:35

Haha it is such a weird stereotype

Ben Sless15:05:43

Besides an extra pair around top level forms, it's the same amount as in method calls, just different order

Darrick Wiebe15:05:24

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...

Sam Ritchie15:05:30

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

catjam 2
☝️ 2
potetm15:05:15

Tbf stacking un-indented parens at the end of a form is pretty uniquely lisp and can look terrifying.

Darrick Wiebe15:05:10

@U017QJZ9M7W We need the Knees over Toes Guy but for parens.

2
potetm15:05:12

You have to learn to rely on structured editing, and trust just isn't something you can explain with numbers

Sam Ritchie15:05:30

(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((fn rec [] rec)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))

2
😄 4
Darrick Wiebe15:05:44

If you have to talk about structured editing, you've already lost, since you are now far off topic.

Sam Ritchie15:05:45

+100 to structured editing

potetm15:05:34

IMO structured editing is fully half the benefit (if not more).

10
jpmonettas15:05:03

just tried that

😄 2
2
😂 2
jpmonettas15:05:18

not sure how accurate it is

ghadi15:05:01

best not to get into dumb arguments about syntax

💯 9
Darrick Wiebe15:05:09

Agree, however what you argue about is not up to you

Darrick Wiebe15:05:29

That chart looks wrong

Darrick Wiebe15:05:02

I think there's not an order of magnitude difference!

potetm15:05:04

Idk "your concerns are dumb" seems a losing strat all the same. Having more motivating ideas/reasons seems better to me.

Alex Miller (Clojure team)15:05:22

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

2
☝️ 10
2
seancorfield15:05:34

You'd need to count { } in non-Clojure code (as well as ( )) to be comparable.

☝️ 8
truestory 4
Alex Miller (Clojure team)15:05:00

once you get used to Clojure's very regular syntax, it is hard not to see every other language as completely arcane

metal 2
☝️ 6
yes 4
Darrick Wiebe15:05:17

Alex is making good points

Alex Miller (Clojure team)15:05:25

but this is how you become a smug lisp weenie :)

🌭 2
ghadi15:05:35

(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)

Alex Miller (Clojure team)15:05:24

and you then lead into treating code as data, macros, etc

Darrick Wiebe15:05:27

No one is saying "your concerns are dumb", just preparing better for these inevitable asides to try to move rapidly beyond tehm.

seancorfield15:05:42

if (x < 0) {
  ...
} else {
  ...
}
// three "parens"
(if (< x 0)
  ...
  ...)
;; two parens

potetm16:05:04

Thanks @U050ECB92. I assumed that's what you meant ftr. I wanted to clarify is all.

Darrick Wiebe16:05:14

Sean, yes, but such simple examples are easily dismissed as cherry picked

Darrick Wiebe16:05:18

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.

seancorfield16:05:48

But it isn't cherry picked. obj.method(arg); vs (.method obj arg) isn't cherry-picked either.

Alex Miller (Clojure team)16:05:50

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)

2
👍 1
Alex Miller (Clojure team)16:05:10

you can mention macros by their purpose (constructing and altering programs themselves) without mentioning "macros"

Alex Miller (Clojure team)16:05:30

and that's very aligned with structural editing as one of the pros of delimited expressions

Alex Miller (Clojure team)16:05:26

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

Alex Miller (Clojure team)16:05:56

but it's an enormous power for expressivity

Alex Miller (Clojure team)16:05:44

by which mean I mean a very high ratio of "what I can say" for "what I have to say it with"

Darrick Wiebe16:05:22

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...

jpmonettas16:05:39

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

seancorfield16:05:03

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

Alex Miller (Clojure team)16:05:49

yes! but it's also helpful to show the reverse - what a Lisper sees in a non Lisp :)

seancorfield16:05:55

When people read Java or C/C++ code, they just skip over a lot of { } and ; and so on...

Darrick Wiebe16:05:43

Like MyParenFactory parenFactory = MyParenFactoryFactory.createParenFactory(); type stuff?

Alex Miller (Clojure team)16:05:45

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

upvote 2
Alex Miller (Clojure team)16:05:19

just in having only expressions and not expressions AND statements alone, it's simpler

Alex Miller (Clojure team)16:05:49

"everything is an expression" and "all expressions nest" is massive

💯 4
1
jpmonettas16:05:14

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

Alex Miller (Clojure team)16:05:21

there are like 10 different value types, and 4 kinds of literal collections, everything nests

Darrick Wiebe16:05:37

This is all great material @U064X3EF3, thank you, it will be very useful.

Darrick Wiebe16:05:39

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 🙂

Sam Ritchie16:05:32

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?”

👍 3
Sam Ritchie16:05:16

As an actual sincere question of course

Sam Ritchie16:05:48

Haha and if they have something good, great, let’s make a macro and use that instead!

😄 2
😂 1
Darrick Wiebe17:05:40

Maybe a breezy "This is my favourite topic! Let's go deep!" is all that's needed.

igrishaev17:05:25

> 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.

daveliepmann17:05:10

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.

2
😂 1
hifumi12317:05:29

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

hifumi12317:05:05

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

wevrem17:05:15

My friendly rebuttal is: “What your language lacks in parenthesis, it more than makes up for in commas, semicolons, and return statements.”

😄 2
kennytilton18:05:22

"Do you worry about all the spaces between words in a newspaper?" Mind you, they may never have seen a newspaper.

Alex Miller (Clojure team)19:05:12

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=cidchWg74Y4https://www.youtube.com/watch?v=KZ8u_sWT9Ls

👀 2
💯 1
Darrick Wiebe19:05:17

Thanks for the extra resources @U064X3EF3 I must have seen these when they were new but it's time to rewatch! 🙂

Alex Miller (Clojure team)19:05:33

He keeps threatening to do part 3 :)

Darrick Wiebe19:05:07

Just needs to focus on it?

seancorfield20:05:00

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!

agile_geek17:05:43

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:

8
Ludger Solbach10:05:00

Here are two other answers from chatGPT.

Darrick Wiebe18:05:16

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.

seancorfield18:05:43

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...

Alex Miller (Clojure team)18:05:31

keep asking till you get the answers you want :)

😄 2
teodorlu11:08:39

> 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.

kennytilton11:08:11

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.

😂 2
diego.videco18:05:09

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).

chucklehead18:05:06

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

diego.videco18:05:00

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

Joshua Suskalo18:05:35

do you store lazy sequences anywhere in the atom?

diego.videco18:05:58

I think most of the stuff in there is realized, but there may be something in there. Mostly things come from instaparse.

Joshua Suskalo18:05:28

See what happens if you call prn-str on the result of the call to swap and return id anyway

Joshua Suskalo18:05:43

that would confirm either a race condition or that it's realizing lazy stuff that's causing it.

diego.videco18:05:27

Seems like that was it

Joshua Suskalo18:05:44

👍 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.

diego.videco19:05:21

is that the best strategy, other than finding where the lazy seq(s) are reside?

Joshua Suskalo19:05:39

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.

Joshua Suskalo19:05:29

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.

Joshua Suskalo19:05:56

This would allow infinite sequences and lazy sequences to be used as a normal part of the library.

diego.videco19:05:31

gotcha, thanks

phronmophobic23:05:14

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.

☝️ 2
diego.videco01:05:10

The data structure is a tree, though, perhaps with postwalk or something?

phronmophobic01:05:35

that’s what the tree-seq call is for.

phronmophobic01:05:58

otherwise, you could just call dorun directly

Ben Lieberman23:05:00

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.

Alex Miller (Clojure team)23:05:12

Gotta do what you gotta do

2
Alex Miller (Clojure team)23:05:02

But depends a lot what you’re doing with it later

skylize00:05:26

> to only benefit from functions like update > Presumably you would also benefit from gaining immutability?

Ben Lieberman00:05:16

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.

igrishaev07:05:41

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.

vemv13:05:04

> 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.

skylize14:05:20

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."

valerauko14:05:30

I hope this triggers a library that does minimal allocation immutable wrappers around java data structures

valerauko14:05:49

I've personally used potemkin's derived maps for similar purposes

Alex Miller (Clojure team)14:05:36

Java has no-allocation immutable wrappers around Java data structures if you want that

vemv14:05:40

> 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

skylize14:05:23

Good point. There was no mention of what the LinkedHashMap contains (and I was just assuming raw data).

🤝 2
didibus22:05:37

This is where it be nice if those were protocols, so you could extend-type update to it yourself.