Fork me on GitHub

What is "strict paredit" as opposed to "paredit"?


paredit is about “Structural editing” strict paredit also protecting integrity of your code disallowing deleting of closing/opening parenthesis if it introduces inbalance s-expression For examle: calva: enable strict paredit by default

👍 2
calva 1

the delete forward says it will prevent you from getting into an unbalanced state but the attached gif has an unbalanced close paren after a few deletions

🤪 1

That is because the recording is cropped. The extra paren is from the outer scope. I'll re-record it, since it does look like Calva paredit is not delivering on its promise.


ah makes sense. the paren is colored black which made it feel even more like being highlighted as mismatched rather than from an outer scope.


Yeah, the rainbows do work for parsing structure. 😃


> It is common to conflate any interactive language prompt with a REPL, but I think it is an important aspect of Lisp REPLs that they are a composition of read-eval-print. I'm not sure I understand what that means. How is e.g. JShell or IRB not a "composition of read-eval-print"?


(I'm not trying to say that JShell or IRB etc. are as good as a full-fledged Lisp REPL — I'm wondering about the "composition" bit in particular.)


I think the point is that read and eval are the same building blocks that the Lisp uses to run your program. This means that you can do everything from the REPL that you can do by writing source files and running your program. In other languages, you can eval expressions at the REPL, but there are certain things that are out-of-bounds, such as adding new classes in Java.


Thanks for the insight! That totally makes sense. :thumbsup::skin-tone-2:


Yes, and in IRB for example, there is no distinction between read and eval (only the latter is provided and it takes a string of code)


Right, I understand. What is the practical benefit of that from the point of view of using the REPL for interactive development, though? I understand the benefit in general (macros etc.)


For me it’s the tight iteration time that comes from working with a running program. I can inspect the working state of the program and re-define functions on the fly.


Note that most seasoned Clojurists recommend that you not usually type at the REPL prompt, but rather that you set things up so you can send forms to your REPL from your editor. You’re still just editing your program source files, but as you do so you’re sending bits and pieces over the REPL and updating your running program in real time.


Totally agreed, that's how I do all of my Clojure development. I guess what I'm really after is this: Imagine I'm a Ruby programmer who makes heavy use of IRB or Pry. A Clojurian comes along and says "Well, IRB or Pry aren't really REPLs. A Lisp REPL is a composition of read-eval-print." The Ruby programmer is like, "huh?", you know? Everything you said is true and that's already what I do, but I guess I'm just trying to figure out a way to explain the "composition" statement in such a way that someone who's used to a "non-compositional" REPL understands what I mean. I haven't used IRB or Pry etc. in a long time, but I think you can also use them to inspect the working state of the program and redefine functions, etc. You can't (easily) send things from the editor window into the REPL, but I guess that's more of a property of Lisps than Lisp REPLs, as such.


I wonder if I'm making any sense. 🙂


I guess it's just quite difficult to explain the benefits of a Clojure (or some other Lisp) REPL without actually showing what writing programs by evaluating things directly from your editor looks like.


Yeah, I’m not really sure what the “compositional” argument is about TBH.


Well, I think your initial comment was bang on.


I mean, if a Ruby dev can achieve a similar effect with IRB, does it matter whether IRB reads code differently from the regular Ruby startup? They might not find “composition” a compelling argument.


Yes, exactly!


I come from a Java background so the differences are a little more stark.


I think what you're saying makes sense, and I don't think there is any benefit to R and E being separate composable pieces, in the limited context of typing strings of code and seeing the result of evaluating it. In that sense, the irb user is not missing out on anything.


except, as @U4ZDX466T said, macros and so on


but if you aren't modifying code as data then a REPL is a REPL even if RE are not decoupled


I would say irb is precisely a REPL where the RE are one thing


Yes, agreed. I believe we're on the same page. 🙂


I guess I gained some new insight into this today... if you try to copy-paste multiple expressions at once into a Python shell, you get an error saying "SyntaxError: multiple statements found while compiling a single statement". I think @U0DTSCAUU's first comment is bang on: with Clojure (and presumably with other Lisps), using the REPL is exactly the same as running the program via other means (e.g. via a main function or something), whereas that isn't (necessarily) the case with other languages. irb does allow you to copy-paste multiple expressions, but it might have other limitations -- not sure. In any case, I think that's what "composition" refers to: since reading is separated from evaluation, the REPL is guaranteed to work the same way as when reading forms from a file, whereas that's not necessarily the case with other languages.


Periodic reminder: there is a #contributions-welcome channel where you can ask for contributions for your OSS projects


(this channel was called #pr-welcome before)


This might be a stupid question but i really wish to understand: Why do libraries and frameworks make so much emphasis on the fact that they might have just a few milliseconds faster startup times? For example does a 3 milliseconds difference in startup time really matter? or are developers just becoming too impatient ?


@kraulain Can you give an example of this?


Off the top of my head i can see this in comparisons of frameworks like quarkus vs micronaut


The difference there isn't ms, it's probably seconds (when compiled to native which both frameworks support). And this can be a significant win in cloud environments.

👍 1

You mean like it will be cheaper to run?


it allows things like on demand scaling - I've worked on microservices that take multiple minutes to spin up


also think e.g. AWS Lambda where you pay per nanosecond or something like that

💯 2
👍 1

if it's seconds instead, you don't need to leave as much idle


Yeah, i see. That makes total financial sense


Starting fast also helps if your language doesnt support a repl... so if you can changed code -> deployed fast... then you can work faster.


the mirage project got ocaml startup times so low, they can feasibly spin up a new http server on demand for a request

🙀 1

oops, I mean start up a container running an http server :D


In servers, 80% of the situations the runtime speed of code don't matter because the DB is a few orders of magnitude slower. And the most remaining 20% optimization gains pennies. What remains is HFT & Google electricity.

👍 1

Yeah, I never thought of that. I think a single ms difference can be a big deal in High-frequency trading


both the jvm and the clojure language make multiple design decisions that trade more resource usage (read: spending more money on your containers per server) and longer startup times (read: needing to leave more servers running because "on demand" spinup is too slow), in exchange for runtime throughput (oops, the bottleneck was IO anyway...)


of course the thing clojure is really great at is optimizing programmer time (you probably spend a lot more on that than you do on hardware)

👍 2

Large legacy clojure not that fun on programmer time. Immutability helps a lot, but no types is shaky business.


Not that with types is so much better... 🙂 makes changes harder.


> 80% of the situations the runtime speed of code don't matter because the DB is a few orders of magnitude slower. Right. Which is why concurrency matters (more?) Some requests can only be sped-up so much. But certainly you'd never want to intentionally slow down other requests

👍 1

• CPU-bound -> multiprocessing • I/O-bound, fast I/O, Limited Number of Connections -> multithreading • I/O-bound, slow I/O, many connections -> asyncio (was reading this the other day)

👍 2

Relevant speed gains is babashka running circles on my antique laptop 🙂


I am trying to come up with a glob function for a library (similar to clj-commons/fs) but there are some nuances. The API: (glob path pattern) Questions: - Should the behavior be recursive by default? Some shells (bash/zsh) and languages (Python, golang) seem to have conflicting opinions on this. - If not recursive by default, what would you make of (glob "." "**/*.md") then, the pattern assumes recursiveness - Should it include hidden dirs by default?

Ben Sless06:01:14

you can also refer to Tcl's implementation, might find it useful


Have you looked at the glob stuff built into java?


It’s a bit buried


yes, that's what I'm building on, but they do not provide any function like this, only the raw building blocks


Looking at ruby's • not recursive by default • **/ makes it recur on the matched dirs • hidden files not included by default Generally ruby api follows unix forefathers wisdom.


Gotcha, I have written this too :)


thanks! so **/ means recursive, instead of an explicit :recursive option right? (double checking)


perfect, thanks

👍 1

That walkFileTree visitor in Java makes me so sad


So bound to mutable state


But IIRC it has hooks that the Stream-based variant doesn’t provide. Anyways, OT


Oh, well this is the off-topic room 🎸


do you mean the DirectoryStream class? afaik this isn't like file-seq, it only lists one level deep


it does take a glob object though, so for non-recursive globbing, I'm using that


@ghadi yeah, I mean, what Stream-based variant were you referring to?



👍 1